2025-10-08

【備忘録】NumPy の警告を例外として扱う

NumPy は、プログラミング言語 Python において数値計算を効率的に行うための拡張モジュールです。効率的な数値計算を行うための型付きの多次元配列(例えばベクトルや行列などを表現できる)のサポートを Python に加えるとともに、それらを操作するための大規模な高水準の数学関数ライブラリを提供しています。

Wikipedia より引用、編集

Numpy の関数計算で無効な引数を指定した場合、RuntimeWarning という警告が出るものの np.float64(nan) を返すだけの場合があります。例を以下に示しました。

import numpy as np

v = -1
np.log(v)
/tmp/ipykernel_10573/3390615159.py:4: RuntimeWarning: invalid value encountered in log
  np.log(v)
np.float64(nan)

np.float64(nan) で処理されて都合が良い時もありますが、例外処理をしたい場合もあります。

警告を例外として扱う方法を Microsoft Copilot に尋ねてみたところ、三つのやり方を教えてくれました。

その中で、最もしっくりきた方法を備忘録にしました。

import warnings

import numpy as np

# NumPyの警告を例外として扱う設定
warnings.filterwarnings("error")

try:
    v = -1
    result = np.log(v)
except Warning as e:
    print(f"警告が例外としてキャッチされました: {e}")
警告が例外としてキャッチされました: invalid value encountered in log

使用した OS 環境は以下のとおりです。

Fedora Linux Workstation 42 x86_64
Python 3.13.7
jupyterlab 4.4.9
numpy 2.2.6

参考サイト

  1. Pythonのwarningsで警告(Warning)を非表示、例外化 | note.nkmk.me

 

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ オープンソースへ
にほんブログ村

オープンソース - ブログ村ハッシュタグ
#オープンソース



このエントリーをはてなブックマークに追加

2025-10-03

倒立振子問題 (CartPole)

Python で強化学習をしようと、いろいろなサイトを調べると CartPole の例をよく見かけます。強化学習をしっかり学んでいる方なら、よく知られた問題なのであらためて調べる必要はないのでしょうが、自分のような初学者にはいまひとつピンときません。

そこで、Gemini に CartPole について説明をしてもらった内容を以下にまとめ直しました。

なお、OpenAI Gym [1] の後継 Gymnasium [2] の利用を前提としているので OpenAI Gym/Gymnasium と記述された部分を Gymnasium だけに直しています。

Gemini がまとめてくれた倒立振子問題 (CartPole)

CartPole(カートポール)とは、台車(カート)の上に取り付けられた棒(ポール)を、台車を左右に動かすことで倒れないように制御する、強化学習の分野における古典的な問題であり、またその問題をシミュレーションする Gymnasium の環境名です。

状態(カートの位置・速度、ポールの角度・角速度)を観測し、台車を左右に動かすという 2 つの行動の中から、できるだけ長くポールを立たせておくことを目的とします。

問題の概要

  • 台車 (Cart): 左右に動かすことができる台車です。
  • (Pole): 台車の上部に垂直に立てられた棒で、この棒が倒れないように制御します。
  • 目的: 棒が倒れないように、台車を左右に動かしてバランスを取ることです。

学習環境

  • CartPole は、Gymnasium が提供する強化学習の学習環境に含まれるタスクです。
  • AI エージェントが学習を進めるための、定番かつシンプルな環境として広く利用されています。

状態 (Observation)

  • 現在の状況は以下の4つの数値で表現されます。
    • 台車の位置
    • 台車の速度
    • ポールの角度
    • ポールの角速度

行動 (Action)

  • AIエージェントは、以下の2つの行動の中から選択します。
    • 台車を左に押す
    • 台車を右に押す

報酬 (Reward)

  • 各ステップで棒が倒れていなければ、+1 の報酬が与えられます。
  • 棒が倒れる、または特定の条件を超えるとエピソードが終了し、報酬が終了します。

終了条件

  • 以下のいずれかの条件を満たすと、1つのエピソード(制御タスクの開始から終了まで)が終了します。
    • ポールの角度が ±12° より大きくなった場合
    • 台車の位置が一定の範囲を超えた場合
    • 制御ステップ数が設定された最大値(v0 では 200 ステップ、v1 では 500 ステップ)を超えた場合

Stable-Baselines3 の PPO モデルで学習

Gymnasiom のドキュメント [3] の Basic Usage(以下)のページでは、Your First RL Program に Hello World 的な最初のコードが示されています。

このコードは、倒立振子問題 (CartPole) を扱った出来合いの環境 CartPole-v1 を用いて、ランダムでアクションを生成して、「環境」の動作を確認できるサンプルです。Gymnasium は強化学習エージェントを訓練するための環境を提供するライブラリなので、この程度しかできないのは仕方がないのですが、どうせならエージェントを用意して強化学習をさせてみたくなります。

そこで、同じ OpenAI 由来の Stable-Baselines3 [3] パッケージの PPO を利用して学習させてみました。

下記の OS 環境で動作確認をしています。

通常は Fedora Linux の環境を示しますが、今回はクリーンな状態から Python venv の仮想環境を作って必要なパッケージをインストールして確認したかったので、OS をインストールしたままの状態で運用している AlmaLinux 10 を使用しました。

AlmaLinux 10 x86_64_v2
python3 3.12.9-2
python3-devel
python3-tkinter
swig 4.3.0-3

Python は本体 (python3) だけでなく、パッケージのビルドに使用する python3-devel と、Matplotlib をインタラクティブモードで使用するために python3-tkinter のパッケージもインストールします。また、swig はパッケージのビルドで必要になります。

開発環境を用意

Python パッケージをインストールする際、一部 C/C++ でコンパイルするものがあるので、次のように開発環境を整えておきます。

$ sudo dnf group install "Development Tools"

Python venv の仮想環境の作成とパッケージのインストール

下記のように例えば rl_cartpole というディレクトリを作成して、その中で venv の仮想環境を作成します。多くの場合、pip のバージョンが古くなっているので、気になる場合はあらかじめアップデートします。

$ mkdir rl_cartpole
$ cd rl_cartpole
$ python -m venv venv
$ source venv/bin/activate
(venv) $ pip install --upgrade pip

以下のように gymnasium と stable-baseline3 パッケージをインストールします。必要な関連パッケージもインストールされます。

(venv) $ pip install gymnasium[box2d] stable-baselines3[extra]

強化学習用サンプル

Gymnasium には出来合いの環境がいくつも用意されています。一覧は下記のとおりです。

import gymnasium as gym
gym.pprint_registry()
===== classic_control =====
Acrobot-v1             CartPole-v0            CartPole-v1
MountainCar-v0         MountainCarContinuous-v0 Pendulum-v1
===== phys2d =====
phys2d/CartPole-v0     phys2d/CartPole-v1     phys2d/Pendulum-v0
===== box2d =====
BipedalWalker-v3       BipedalWalkerHardcore-v3 CarRacing-v3
LunarLander-v3         LunarLanderContinuous-v3
===== toy_text =====
Blackjack-v1           CliffWalking-v1        CliffWalkingSlippery-v1
FrozenLake-v1          FrozenLake8x8-v1       Taxi-v3
===== tabular =====
tabular/Blackjack-v0   tabular/CliffWalking-v0
===== None =====
Ant-v2                 Ant-v3                 GymV21Environment-v0
GymV26Environment-v0   HalfCheetah-v2         HalfCheetah-v3
Hopper-v2              Hopper-v3              Humanoid-v2
Humanoid-v3            HumanoidStandup-v2     InvertedDoublePendulum-v2
InvertedPendulum-v2    Pusher-v2              Reacher-v2
Swimmer-v2             Swimmer-v3             Walker2d-v2
Walker2d-v3
===== mujoco =====
Ant-v4                 Ant-v5                 HalfCheetah-v4
HalfCheetah-v5         Hopper-v4              Hopper-v5
Humanoid-v4            Humanoid-v5            HumanoidStandup-v4
HumanoidStandup-v5     InvertedDoublePendulum-v4 InvertedDoublePendulum-v5
InvertedPendulum-v4    InvertedPendulum-v5    Pusher-v4
Pusher-v5              Reacher-v4             Reacher-v5
Swimmer-v4             Swimmer-v5             Walker2d-v4
Walker2d-v5

今回は CartPole-v1 の学習環境を試します。gym.make で以下のように CartPole-v1 を指定することで Gymnasium の仕様に準拠した CartPole-v1 の学習環境 env を生成できます。

env = gym.make("CartPole-v1")

先に触れた Gymnasiom のドキュメント [3] の Basic Usage のページ、Your First RL Programで示されているコードをベースにして、Stable-Baselines3 パッケージの PPO を利用して学習できるようにしたのが下記のコードです。

PPO では、多層パーセプトロン (MLP) ベースの方策と価値関数を使う MlpPolicy を指定しています。

このコードを適当なファイル名(例: cartpole_rl.py)で保存して学習を実行します。

cartpole_rl.py
import os

import gymnasium as gym
from stable_baselines3 import PPO
from stable_baselines3.common.monitor import Monitor

if __name__ == "__main__":
    # ログフォルダの準備
    dir_log = "./logs/"
    os.makedirs(dir_log, exist_ok=True)

    # 学習環境の準備
    env = gym.make("CartPole-v1", render_mode="human")
    env = Monitor(env, dir_log)  # Monitorの利用

    # モデルの準備
    model = PPO("MlpPolicy", env, verbose=True)

    # 学習の実行
    model.learn(total_timesteps=50000)

    # 推論の実行
    obs, info = env.reset()
    print(f"Starting observation: {obs}")

    episode_over = False
    total_reward = 0

    while not episode_over:
        action, _ = model.predict(obs, deterministic=True)

        obs, reward, terminated, truncated, info = env.step(action)

        total_reward += reward
        episode_over = terminated or truncated

    print(f"Episode finished! Total reward: {total_reward}")
    env.close()
(venv) $ python cartpole_rl.py

CartPole の棒が揺れながらバランスを取る様子がウィンドウに表示され、学習後に推論が実行されます。

env = gym.make("CartPole-v1", render_mode="human") で学習中にアニメーション表示
出力例
Using cpu device
Wrapping the env in a DummyVecEnv.
/home/bitwalk/MyProjects/rl_cartpole/venv/lib64/python3.12/site-packages/pygame/pkgdata.py:25: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
  from pkg_resources import resource_stream, resource_exists
---------------------------------
| rollout/           |          |
|    ep_len_mean     | 22.1     |
|    ep_rew_mean     | 22.1     |
| time/              |          |
|    fps             | 47       |
|    iterations      | 1        |
|    time_elapsed    | 43       |
|    total_timesteps | 2048     |
---------------------------------
-----------------------------------------
| rollout/                |             |
|    ep_len_mean          | 29          |
|    ep_rew_mean          | 29          |
| time/                   |             |
|    fps                  | 46          |
|    iterations           | 2           |
|    time_elapsed         | 87          |
|    total_timesteps      | 4096        |
| train/                  |             |
|    approx_kl            | 0.008222323 |
|    clip_fraction        | 0.0791      |
|    clip_range           | 0.2         |
|    entropy_loss         | -0.687      |
|    explained_variance   | -0.00142    |
|    learning_rate        | 0.0003      |
|    loss                 | 6.95        |
|    n_updates            | 10          |
|    policy_gradient_loss | -0.0101     |
|    value_loss           | 54.3        |
-----------------------------------------
...
(途中省略)
...
-----------------------------------------
| rollout/                |             |
|    ep_len_mean          | 398         |
|    ep_rew_mean          | 398         |
| time/                   |             |
|    fps                  | 47          |
|    iterations           | 25          |
|    time_elapsed         | 1079        |
|    total_timesteps      | 51200       |
| train/                  |             |
|    approx_kl            | 0.004092879 |
|    clip_fraction        | 0.043       |
|    clip_range           | 0.2         |
|    entropy_loss         | -0.489      |
|    explained_variance   | 0.0157      |
|    learning_rate        | 0.0003      |
|    loss                 | 0.0149      |
|    n_updates            | 240         |
|    policy_gradient_loss | -0.00138    |
|    value_loss           | 0.021       |
-----------------------------------------
Starting observation: [ 0.0244522   0.03066805  0.03330757 -0.04372405]
Episode finished! Total reward: 500.0

出力される学習に関連する情報について、Gemini に教えてもらった内容を以下にまとめました。

  • rollout
  • データ収集・エピソード結果
  • このセクションは、エージェントが環境と相互作用してデータを収集した結果を示します。
    • ep_len_mean
      • 平均エピソード長 (Episode Length Mean)
      • 直近のエピソードのステップ数の平均値です。デイトレ環境の場合、1日のティック数、またはエピソードが終了するまでの平均時間ステップを示します。
    • ep_rew_mean
      • 平均エピソード報酬 (Episode Reward Mean)
      • 直近のエピソードでエージェントが得た累積報酬(総利益)の平均値です。この値の増加が、学習が順調に進んでいるかどうかの最も重要な指標となります。
  • time
  • 時間・実行速度に関する指標
  • このセクションは、学習プロセス自体の効率と進捗を示します。
    • fps
      • フレーム毎秒 (Frames Per Second)
      • 1 秒あたりに処理されたタイムステップの数。学習の処理速度を示し、高いほど効率的です。
    • iterations
      • イテレーション数
      • PPO の更新サイクルが何回実行されたかを示す回数です。通常、total_timesteps が n_steps * n_envs(データバッファサイズ)に達するごとに 1 回インクリメントされます。
    • time_elapsed
      • 経過時間 (秒)
      • 学習開始からの合計経過時間です。
    • total_timesteps
      • 総タイムステップ数
      • 学習開始から現在までに環境と相互作用した合計のステップ数です。
  • train
  • 学習の質に関する指標
  • このセクションの指標は、モデル(ポリシーと価値関数)がどれだけうまくデータに適合し、ポリシーの更新が適切に行われているかを示します。
    • approx_kl
      • 近似 KL ダイバージェンス (Approximate KL Divergence)
      • 更新後の新しいポリシーと、更新前の古いポリシーとの間の距離を示します。PPO は、ポリシーが急激に変化するのを防ぐため、この値が大きくなりすぎないように制御します。
    • clip_fraction
      • クリップされた割合 (Clipped Fraction)
      • 勾配更新時に、PPO のクリッピング機構によって損失が制限されたサンプルデータの割合です。この値が高すぎると、クリッピングが強すぎて学習が進まない可能性があります。
    • clip_range
      • クリップ範囲
      • PPO のハイパーパラメータであり、ポリシー更新時の比率(rt(θ))を制限する範囲(例:[1−ϵ, 1+ϵ] の ϵ)。ここでは ϵ=0.2 と設定されています。
    • entropy_loss
      • エントロピー損失
      • エージェントの行動のランダム性(探索の度合い)に関する損失項。値が負で、絶対値が大きいほど、ポリシーの行動がランダムで多様であることを示します。徐々に 0 に近づくことが期待されます。
    • explained_variance
      • 説明された分散
      • 価値関数 (Value Function) が、実際の累積報酬(リターン)をどれだけうまく予測できているかを示す指標。1 に近いほど、予測精度が高いことを示します。負の値は、予測が平均より悪いことを示し、モデルの不安定さを示唆します。
    • learning_rate
      • 学習率
      • ニューラルネットワークの重みを更新する際のステップサイズです。PPO では、学習の進行とともに線形に減衰(スケジューリング)されることが一般的です。
    • loss
      • 全体の損失関数(ポリシー損失 + 価値関数損失 + エントロピー損失など)の合計。
      • この値が小さくなるほど学習は進んでいると見なされますが、個別の損失に比べて解釈は難しいです。
    • n_updates
      • これまでの総更新回数。
      • Iteration 数と n_epochs (1つの Iteration 内でモデルを更新する回数) の積です。
    • policy_gradient_loss
      • ポリシー勾配損失
      • PPO の主要な損失関数で、ポリシー(行動戦略)の更新に使用されます。負の値を示し、負の絶対値が大きいほど、ポリシーが更新によって報酬を増加させる方向に強く引っ張られていることを意味します。
    • value_loss
      • 価値損失
      • 価値関数(状態の価値を予測するネットワーク)の更新に使用される損失です。この値が低いほど、価値関数が正確に状態の価値を予測できていることを示します。

学習曲線の確認

PPO を利用して学習をできるようにしたコード(cartpole_rl.py)では、gym.make で生成した環境 envMonitor に通しています。この Monitor は、Stable-Baselines3 パッケージが提供する Gymnasium 環境向けのモニターラッパーで、エピソード報酬、長さ、時間、その他のデータを把握するために使用します。

このサンプルでは、dir_log (= "./logs/") 内にログを保存するように設定しています。

    # 学習環境の準備
    env = gym.make("CartPole-v1", render_mode="human")
    env = Monitor(env, dir_log)  # Monitorの利用

学習を実行した後、dir_log (= "./logs/") 内を確認すると monitor.csv というファイルがあり、最初の行がコメント行になっていて、以下 r(エピソード報酬)、l(エピソードの長さ)、t(時間)が記録されています。

(venv) $ ls logs
monitor.csv
(venv) $ cat logs/monitor.csv
#{"t_start": 1759387094.2588797, "env_id": "CartPole-v1"}
r,l,t
12.0,12,8.555453
18.0,18,8.94126
18.0,18,9.327632
...
(以下省略)

報酬トレンド

CartPole-v1 では 500 ステップが上限なので、最大ステップまで棒 (Pole) が倒れなければ報酬は +500 になり、これが最大です。すなわち、報酬を最大化することが、できるだけ長くポールを立たせておくという目的と一致しています。

報酬がどのように増えるかをエピソード順にプロットすることで、学習曲線を確認できます。

plot_monitor_reward.py
import os
import pandas as pd
import matplotlib.pyplot as plt

if __name__ == "__main__":
    # monitor.csv の読み込み
    dir_log = "./logs"
    name_log = "monitor.csv"
    # 最初の行の読み込みを除外
    df = pd.read_csv(os.path.join(dir_log, name_log), skiprows=[0])

    # 報酬のプロット
    plt.plot(df["r"])
    plt.xlabel("episode")
    plt.ylabel("reward")
    plt.grid()
    plt.tight_layout()
    plt.show()
(venv) $ python plot_monitor_reward.py
monitor.csv のエピソード順の r(報酬)トレンド

この例では、エピソードが 350 回に近づくと報酬が 500 になっています。350 回程度のエピソード(学習回数)で、モデルは報酬を最大化できた、すなわち、棒を倒さないコツを学習できたと言えるでしょう。

なお、エピソードの回数とエピソードの長さの積の累積が、cartpole_rl.py における下記の total_timesteps を超えない範囲で学習が行われますので、もう少し確認したければ数値を大きくする必要があります。

    # 学習の実行
    model.learn(total_timesteps=50000)

まとめ

強化学習を利用したい、というニーズあって、そのために Gymnasium であれこれ環境を作っています。最近、少し行き詰まってきたと感じたので、かき集めた知識を整理するためにも一旦立ち止まって、CartPole-v1 のような出来合いの環境を試してみました。

書いてまとめることによって自分の理解の整理ができて、行き詰まり感も解消できたことは良かったのですが、興味がある部分を中心にまとめてしまったきらいがあります。おいおい足りない部分を書き足します。

 

参考サイト

  1. openai/gym: A toolkit for developing and comparing reinforcement learning algorithms.
  2. Farama-Foundation/Gymnasium (formerly Gym)
  3. Gymnasium Documentation
  4. DLR-RM/stable-baselines3: PyTorch version of Stable Baselines
  5. Stable-Baselines3 Docs - Reliable Reinforcement Learning Implementations
  6. Monitor Wrapper — Stable Baselines3 documentation
  7. Stable Baselines 3 入門 (1) - 強化学習アルゴリズム実装セット|npaka
  8. Stable Baselines 3 入門 (2) - Monitor|npaka

 

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ オープンソースへ
にほんブログ村

オープンソース - ブログ村ハッシュタグ
#オープンソース



このエントリーをはてなブックマークに追加

2025-09-30

日経平均株価の構成銘柄を取得

日経平均株価は、日本経済新聞社が算出・公表している日本の株式市場の代表的な株価指数の一つです。東京証券取引所プライム市場に上場している約 2,000 銘柄の株式のうち、取引が活発で流動性が高い 225 銘柄を、日本経済新聞社が選定し算出します。構成銘柄は 4 月と 10 月の年 2 回、上限各 3 銘柄の定期入れ替えが実施されています。

Wikipedia より引用、編集

日経平均株価の構成銘柄を Python で取得しようと Jupyter Lab 上で、参考サイト [1] に従って試したところ、以下のようにエラーが出て取得できませんでした。

dfs = pd.read_html("https://indexes.nikkei.co.jp/nkave/index/component")
---------------------------------------------------------------------------
HTTPError                                 Traceback (most recent call last)

...
(途中省略)
...

    612 def http_error_default(self, req, fp, code, msg, hdrs):
--> 613     raise HTTPError(req.full_url, code, msg, hdrs, fp)

HTTPError: HTTP Error 403: Forbidden

ところが、端末エミュレータ上で同じ URL を wget コマンドで同じサイトを取得してみたところ、難なく出来てしまいました。

上記 Pandas で読み込むやり方でも、何か工夫をすれば読み込めるのかもしれませんが、とりあえず Python から wget コマンドを実行して、読み込みたいサイトを一旦ローカルファイルに保存してから、そのファイルを Pandas で読み込むことで、やりたかったことをできるようになったので、備忘録にしました。

下記の OS 環境で動作確認をしています。

Fedora Linux Workstation 42 x86_64
Python 3.13.7
beautifulsoup4 4.14.2
html5lib 1.1
JupyterLab 4.4.9
lxml 6.0.2
pandas 2.3.3
wget2 2.2.0-5 (rpm)

サンプル

以下、Jupyter Lab 上で実行することを想定しています。

最初に利用するライブラリをまとめてインポートします。

import os
import subprocess

import pandas as pd

外部コマンドを実行する関数です。ピッタリな用途の関数が公開されていたので、そのまま流用させていただきました。

# Reference:
# https://brightdata.jp/blog/各種ご利用方法/wget-with-python
def execute_command(command):
    """
    Execute a CLI command and return the output and error messages.

    Parameters:
        - command (str): The CLI command to execute.

    Returns:
        - output (str): The output generated by the command.
        - error (str): The error message generated by the command, if any.
    """
    try:
        # execute the command and capture the output and error messages
        process = subprocess.Popen(
            command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
        )
        output, error = process.communicate()
        output = output.decode("utf-8")
        error = error.decode("utf-8")
        # return the output and error messages
        return output, error
    except Exception as e:
        # if an exception occurs, return the exception message as an error
        return None, str(e)

日経平均株価の構成銘柄をデータフレームで取得する処理です。

# wget コマンドで対象サイトをダウンロード
file = "component"
# ファイルが存在していれば削除 
if os.path.exists(file):
    os.remove(file)
output, error = execute_command(f"wget https://indexes.nikkei.co.jp/nkave/index/{file}")

# 保存された HTML ファイルを Pandas で読み込む
dfs = pd.read_html(file)
# 複数のデータフレームを結合して、コード列でソート
df = pd.concat(dfs).sort_values("コード", ignore_index=True)
df

 

参考サイト

  1. Python, pandasでwebページの表(htmlのtable)をスクレイピング | note.nkmk.me
  2. 構成銘柄一覧 - 日経平均プロフィル

 

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ オープンソースへ
にほんブログ村

オープンソース - ブログ村ハッシュタグ
#オープンソース



このエントリーをはてなブックマークに追加

2025-09-16

Ta-Lib を Python で利用する

TA-Lib, Technical Analysis Library は 2001 年にリリースされ、20 年以上経った今でも広く利用されている著名なアルゴリズムを提供しています。コードは安定しており、長年にわたる検証を経ています。200 以上のテクニカル指標をサポートしており、API は C/C++ で記述されており Python ラッパー (wrapper) も提供されています。TA-Lib は BSD License (BSD-2-Clause license) の元で配布されているオープンソースのライブラリです。

以前は Linux 上で TA-Lib の Python 用パッケージを pip でインストールしてもビルドが必要で、しかもエラーでビルドできませんでした。自力でエラーを解決できなかったので TA-Lib の利用を避けていました。しかし最近の TA-Lib のバージョンの Python 用パッケージでは難なくインストールできることが判りました。

そこで今更ですが TA-Lib の使い方をおぼえようと、Jupyter Lab 上でテクニカル指標のいくつかをプロットしてみたので、備忘録的に内容をまとめました。

下記の OS 環境で動作確認をしています。

Fedora Linux Workstation 42 x86_64
Python 3.13.7
JupyterLab 4.4.7
matplotlib 3.10.6
mplfinance 0.12.10b0
numpy 2.3.3
pandas 2.3.2
ta-lib 0.6.7
yfinance 0.2.65

サンプル

ライブラリをインポート

最初に利用するライブラリをまとめてインポートします。

import matplotlib.font_manager as fm
import matplotlib.pyplot as plt
import mplfinance as mpf
import numpy as np
import pandas as pd
import yfinance as yf
from talib import BBANDS, MACD, MFI, MOM, OBV, RSI, SAR, STOCH

yfinance で日経平均株価指数の過去データを取得

サンプルとして、今年の 1 月から半年間の日足データを取得します。

symbol = "^N225"
ticker = yf.Ticker(symbol)
df = ticker.history(start="2025-01-01", end="2025-07-01", interval="1d")

サンプル期間より少し古いデータからも取得しておきます。これでテクニカル指標を算出して、サンプルの期間の最初から指標がプロットされるようにします。

df2 = ticker.history(start="2024-10-01", end="2025-07-01", interval="1d")

mplfinance でチャートを作成

サンプル期間の日足データをローソク足チャートと出来高の棒グラフを並べてプロットしました。

fig = plt.figure(figsize=(8, 4))
ax = dict()
n = 2
gs = fig.add_gridspec(
    n, 1, wspace=0.0, hspace=0.0, height_ratios=[3 if i == 0 else 1 for i in range(n)]
)
for i, axis in enumerate(gs.subplots(sharex="col")):
    ax[i] = axis
    ax[i].grid()

mpf.plot(
    df,
    type="candle",
    style="default",
    datetime_format="%m/%d",
    xrotation=0,
    ax=ax[0],
    volume=ax[1],
)
ax[0].set_title(f"{ticker.info['longName']} ({symbol})")

plt.tight_layout()
# plt.savefig("screenshots/n225_default.png")
plt.show()

Bollinger bands

過去 20 日間の移動平均、移動標準偏差 +3σ, +2σ, +1σ, mean, -1σ, -2σ, -3σ でボリンジャーバンドを作成しました。

fig, ax = plt.subplots(figsize=(8, 3))

# BBANDS - Bollinger Bands
# upperband, middleband, lowerband = BBANDS(real, timeperiod=5, nbdevup=2, nbdevdn=2, matype=0)
period = 20
mv_upper_1, mv_mean, mv_lower_1 = BBANDS(df2["Close"], period, 1, 1)
mv_upper_2, _, mv_lower_2 = BBANDS(df2["Close"], period, 2, 2)
mv_upper_3, _, mv_lower_3 = BBANDS(df2["Close"], period, 3, 3)

apds = [
    mpf.make_addplot(
        mv_upper_3[df.index],
        width=1,
        color="C0",
        linestyle="dotted",
        label="+3σ",
        ax=ax,
    ),
    mpf.make_addplot(
        mv_upper_2[df.index],
        width=0.9,
        color="C1",
        linestyle="dashdot",
        label="+2σ",
        ax=ax,
    ),
    mpf.make_addplot(
        mv_upper_1[df.index],
        width=0.75,
        color="C2",
        linestyle="dashed",
        label="+1σ",
        ax=ax,
    ),
    mpf.make_addplot(
        mv_mean[df.index],
        width=1,
        color="C3",
        linestyle="solid",
        label="Mean",
        ax=ax,
    ),
    mpf.make_addplot(
        mv_lower_1[df.index],
        width=0.75,
        color="C4",
        linestyle="dashed",
        label="-1σ",
        ax=ax,
    ),
    mpf.make_addplot(
        mv_lower_2[df.index],
        width=0.9,
        color="C5",
        linestyle="dashdot",
        label="-2σ",
        ax=ax,
    ),
    mpf.make_addplot(
        mv_lower_3[df.index],
        width=1,
        color="C6",
        linestyle="dotted",
        label="-3σ",
        ax=ax,
    ),
]
mpf.plot(
    df,
    type="candle",
    style="default",
    addplot=apds,
    datetime_format="%m/%d",
    xrotation=0,
    update_width_config=dict(candle_linewidth=0.75),
    ax=ax,
)
ax.grid()
ax.legend(fontsize=7)
ax.set_title(
    f"{ticker.info['longName']} ({symbol})\nwith Bollinger Bands (period={period}days)"
)

plt.tight_layout()
# plt.savefig("screenshots/n225_talib_bbands.png")
plt.show()

Parabolic SAR

AF step=0.02, max=0.2 で Parabolic SAR をプロットしました。上昇下降トレンドの情報が無いので、灰色の丸点でプロットしました。

fig, ax = plt.subplots(figsize=(8, 3))

# SAR - Parabolic SAR
# real = SAR(high, low, acceleration=0, maximum=0)
af_step = 0.02
af_max = 0.2
sar = SAR(df2["High"], df2["Low"], af_step, af_max)

apds = [
    mpf.make_addplot(
        sar[df.index],
        type="scatter",
        marker='o',
        markersize=3,
        color="darkgray",
        ax=ax,
    ),
]
mpf.plot(
    df,
    type="candle",
    style="default",
    addplot=apds,
    datetime_format="%m/%d",
    xrotation=0,
    update_width_config=dict(candle_linewidth=0.75),
    ax=ax,
)
ax.grid()
ax.set_title(
    f"{ticker.info['longName']} ({symbol})\nwith Parabolic SAR (AF step={af_step}, max={af_max})"
)

plt.tight_layout()
# plt.savefig("screenshots/n225_talib_sar.png")
plt.show()

Momentum

過去 10 日間のデータでモメンタムを算出しました。

fig = plt.figure(figsize=(8, 4))
ax = dict()
n = 2
gs = fig.add_gridspec(
    n, 1, wspace=0.0, hspace=0.0, height_ratios=[2 if i == 0 else 1 for i in range(n)]
)
for i, axis in enumerate(gs.subplots(sharex="col")):
    ax[i] = axis
    ax[i].grid()

# MOM - Momentum
# real = MOM(real, timeperiod=10)
period = 10
mom = MOM(df2["Close"], period)
apds = [
    mpf.make_addplot(
        mom[df.index],
        width=1,
        color="C0",
        linestyle="solid",
        ax=ax[1],
    ),
]
mpf.plot(
    df,
    type="candle",
    style="default",
    addplot=apds,
    datetime_format="%m/%d",
    xrotation=0,
    ax=ax[0],
)
ax[1].set_ylabel("Momentum")
ax[0].set_title(
    f"{ticker.info['longName']} ({symbol})\nwith Momentum (period={period}days)"
)

plt.tight_layout()
# plt.savefig("screenshots/n225_talib_mom.png")
plt.show()

RSI, Relative Strength Index

過去 14 日間のデータで RSI を算出しました。

fig = plt.figure(figsize=(8, 4))
ax = dict()
n = 2
gs = fig.add_gridspec(
    n, 1, wspace=0.0, hspace=0.0, height_ratios=[2 if i == 0 else 1 for i in range(n)]
)
for i, axis in enumerate(gs.subplots(sharex="col")):
    ax[i] = axis
    ax[i].grid()

# RSI - Relative Strength Index
# real = RSI(real, timeperiod=14)
period = 14
rsi = RSI(df2["Close"], period)
apds = [
    mpf.make_addplot(
        rsi[df.index],
        width=1,
        color="C0",
        linestyle="solid",
        ax=ax[1],
    ),
]
mpf.plot(
    df,
    type="candle",
    style="default",
    addplot=apds,
    datetime_format="%m/%d",
    xrotation=0,
    ax=ax[0],
)
ax[0].set_title(f"{ticker.info['longName']} ({symbol})\nwith RSI (period={period}days)")
ax[1].set_ylabel("RSI")
ax[1].set_ylim(0, 100)
ax[1].axhline(30, color="black", linewidth=0.5)
ax[1].axhline(70, color="black", linewidth=0.5)

plt.tight_layout()
# plt.savefig("screenshots/n225_talib_rsi.png")
plt.show()

Stochastic oscillator

スローストキャスティクスをデフォルトのパラメータのままでプロットしています。

fig = plt.figure(figsize=(8, 4))
ax = dict()
n = 2
gs = fig.add_gridspec(
    n, 1, wspace=0.0, hspace=0.0, height_ratios=[2 if i == 0 else 1 for i in range(n)]
)
for i, axis in enumerate(gs.subplots(sharex="col")):
    ax[i] = axis
    ax[i].grid()

# STOCH - Stochastic
# slowk, slowd = STOCH(high, low, close, fastk_period=5, slowk_period=3, slowk_matype=0, slowd_period=3, slowd_matype=0)
slowk, slowd = STOCH(df2["High"], df2["Low"], df2["Close"])

apds = [
    mpf.make_addplot(
        slowk[df.index],
        width=1,
        color="C0",
        linestyle="solid",
        label="Slow%K",
        ax=ax[1],
    ),
    mpf.make_addplot(
        slowd[df.index],
        width=1,
        color="C1",
        linestyle="solid",
        label="Slow%D",
        ax=ax[1],
    ),
]
mpf.plot(
    df,
    type="candle",
    style="default",
    addplot=apds,
    datetime_format="%m/%d",
    xrotation=0,
    ax=ax[0],
)
ax[0].set_title(f"{ticker.info['longName']} ({symbol})\nwith Stochastic oscillator")
ax[1].set_ylabel("Stochastic")
ax[1].set_ylim(0, 100)
ax[1].axhline(20, color="black", linewidth=0.5)
ax[1].axhline(80, color="black", linewidth=0.5)
ax[1].legend(fontsize=7)

plt.tight_layout()
# plt.savefig("screenshots/n225_talib_stoch.png")
plt.show()

MACD, Moving Average Convergence Divergence

MACD もデフォルトのパラメータでプロットしています。MACD のヒストグラムは正負で色を変えたかったのですが、すぐに出来なかったので単色にしてしまいました。

fig = plt.figure(figsize=(8, 4))
ax = dict()
n = 2
gs = fig.add_gridspec(
    n, 1, wspace=0.0, hspace=0.0, height_ratios=[2 if i == 0 else 1 for i in range(n)]
)
for i, axis in enumerate(gs.subplots(sharex="col")):
    ax[i] = axis
    ax[i].grid()

# MACD - Moving Average Convergence/Divergence
# macd, macdsignal, macdhist = MACD(real, fastperiod=12, slowperiod=26, signalperiod=9)
period_fast = 12
period_slow = 26
period_signal = 9
macd, signal, macdhist = MACD(df2["Close"], period_fast, period_slow, period_signal)

apds = [
    mpf.make_addplot(
        macd[df.index],
        width=1,
        color="C0",
        linestyle="solid",
        label="MACD",
        ax=ax[1],
    ),
    mpf.make_addplot(
        signal[df.index],
        width=1,
        color="C1",
        linestyle="solid",
        label="Signal",
        ax=ax[1],
    ),
    mpf.make_addplot(
        macdhist[df.index],
        type="bar",
        color="C2",
        ax=ax[1],
    ),
]
mpf.plot(
    df,
    type="candle",
    style="default",
    addplot=apds,
    datetime_format="%m/%d",
    xrotation=0,
    ax=ax[0],
)
ax[0].set_title(
    f"{ticker.info['longName']} ({symbol})\nwith MACD [{period_fast}, {period_slow}, {period_signal}]"
)
ax[1].set_ylabel("MACD")
ax[1].legend(fontsize=7)

plt.tight_layout()
# plt.savefig("screenshots/n225_talib_macd.png")
plt.show()

OBV, On Balance Volume

OBV は終値と出来高から算出する指標です。

fig = plt.figure(figsize=(8, 5))
ax = dict()
n = 3
gs = fig.add_gridspec(
    n, 1, wspace=0.0, hspace=0.0, height_ratios=[2 if i == 0 else 1 for i in range(n)]
)
for i, axis in enumerate(gs.subplots(sharex="col")):
    ax[i] = axis
    ax[i].grid()

# OBV - On Balance Volume
# real = OBV(close, volume)
obv = OBV(df2["Close"], df2["Volume"])

apds = [
    mpf.make_addplot(
        obv[df.index],
        width=1,
        color="C0",
        linestyle="solid",
        ax=ax[2],
    ),
]
mpf.plot(
    df,
    type="candle",
    style="default",
    addplot=apds,
    datetime_format="%m/%d",
    xrotation=0,
    ax=ax[0],
    volume=ax[1],
)
ax[0].set_title(f"{ticker.info['longName']} ({symbol})\nwith OBV")
ax[2].set_ylabel("OBV")

plt.tight_layout()
# plt.savefig("screenshots/n225_talib_obv.png")
plt.show()

MFI, Money Flow Index

MFI も、株価と出来高から算出する指標です。

fig = plt.figure(figsize=(8, 5))
ax = dict()
n = 3
gs = fig.add_gridspec(
    n, 1, wspace=0.0, hspace=0.0, height_ratios=[2 if i == 0 else 1 for i in range(n)]
)
for i, axis in enumerate(gs.subplots(sharex="col")):
    ax[i] = axis
    ax[i].grid()

# MFI - Money Flow Index
# NOTE: The MFI function has an unstable period.
# real = MFI(high, low, close, volume, timeperiod=14)
period = 14
mfi = MFI(df2["High"], df2["Low"], df2["Close"], df2["Volume"], period)

apds = [
    mpf.make_addplot(
        mfi[df.index],
        width=1,
        color="C0",
        linestyle="solid",
        ax=ax[2],
    ),
]
mpf.plot(
    df,
    type="candle",
    style="default",
    addplot=apds,
    datetime_format="%m/%d",
    xrotation=0,
    ax=ax[0],
    volume=ax[1],
)
ax[0].set_title(f"{ticker.info['longName']} ({symbol})\nwith MFI (period={period}days)")
ax[2].axhline(20, color="black", linewidth=0.5)
ax[2].axhline(80, color="black", linewidth=0.5)
ax[2].set_ylabel("MFI")
ax[2].set_ylim(0, 100)

plt.tight_layout()
# plt.savefig("screenshots/n225_talib_mfi.png")
plt.show()

参考サイト

  1. TA-Lib - Technical Analysis Library
  2. TA-Lib/ta-lib: TA-Lib (Core C Library)
  3. TA-Lib/ta-lib-python: Python wrapper for TA-Lib
  4. TA-Lib · PyPI

 

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ オープンソースへ
にほんブログ村

オープンソース - ブログ村ハッシュタグ
#オープンソース



このエントリーをはてなブックマークに追加

2025-07-27

deepin 25 をインストール

deepin(深度操作系统)は、中国に拠点をおく UnionTech(统信软件)の子会社である Wuhan Deepin Technology Co., Ltd.(武汉深之度科技有限公司)が開発している Linux ディストロです。デスクトップ環境に Qt を利用した独自の DDE (Deepin Desktop Environment) を採用しています。

deepin は DDE をデスクトップ環境にした Debian ベースのディストロだと単純に思っていましたが、そんな単純なプロジェクトではないようです。Deepin_Profile | DeepinWiki によると、2022 年以降の開発計画では、Linux カーネルをベースに独自のディストロへの展開を目指しているように見えます。一部を抜粋しました。

Plan of Deepin after 2022 year

  • based on kernel
graph LR 1(Linux Kernel)---2(Deepin) 2(Deepin)---3(UniontechOS) 2(Deepin)---4(Other OS)

deepin 25

2025-06-21 に deepin 25 がリリースされたので、遅ればせながら評価用の PC にインストールしてみました。インストール用の ISO イメージは下記からダウンロードできます。

リリースノートによると、このリリースの新機能は以下の 5 点になっています。

  1. DDE 7.0
    • DDE は Qt で記述された deepin 独自のデスクトップ環境です。
    • QMLを使用して数多くのデスクトップコンポーネントを再構築しました。
  2. UOS AI
    • UOS AI という音声 AI アシスタントが利用可能になりました。
    • 話すだけで操作
      • システムが聴き取ります。画面の明るさを調整したり、迅速に会議を作成したい場合、自然言語でシステムにコマンドを発行してみてください!
      • 音声でもテキストでもシステムは理解し、面倒なメニュークリックから解放されます。
    • デュアルモードエンジン
      • DeepSeek、Baidu Qianfan、iFlytek Sparkなどの主要なオンライン大規模モデルに接続し、クラウドコンピューティングのパワーを活用できます。
    • エージェントストア
      • AIアシスタントの新しい「エージェントストア」から、さまざまな分野のエージェントをダウンロードし、AIの応用シーンを拡大し、よりパーソナライズされたAIを実現できます。
  3. Solid (deepin Immutable System)
    • Solid というイミュータブル(読み取り専用)のシステムが導入されました。
    • 読み取り専用保護
      • /usr/bin などのシステムコアディレクトリを読み取り専用として強制マウントすることで、システムはユーザーによる誤操作やマルウェアによるシステムコアの改変を根本的に阻止し長期的に安定した動作を保証します。
    • セカンドレベルのスナップショット
      • システム更新のたびに、システムは自動的にバックアップスナップショットを作成します。更新中に予期せぬ問題が発生した場合でも、再起動時に最後の正常な状態に自動的にロールバックするため、「更新によるシステム破損」の不安を解消します。
    • 安心復元
      • 公共のコンピュータや展示用デバイスなどのシナリオ向けに設計された「安心復元」機能は、再起動時に使用履歴を自動的に削除するほか、セキュリティと効率のバランスを調整するためのホワイトリスト設定もサポートしています。
  4. Linyaps: Universal Adaptability,Deep Ecosystem Roots
    • Linyaps はコンテナ化されたサンドボックス技術を採用し、アプリケーションとその依存関係ライブラリを完全に隔離します。
      • 単一のLinyapパッケージは Debian、Ubuntu、Arch、Fedora、openEuler を含む7つの主要なディストリビューション上で直接実行可能です。
      • マルチアーキテクチャ対応
        • AMD64、ARM64、LoongArch64など、主要な CPU アーキテクチャに対する包括的なサポートを提供しています。
        • Linyaps のエコシステムには、オフィス、開発、エンターテインメントなど多様なニーズに対応する 5000 を超えるソフトウェアパッケージがホストされています。
  5. Community Power Tools
    • Distrobox サブシステム
      • Debian 12、Ubuntu (20.04/24.04)、Arch、Fedora (41/42) などの主要なディストリビューションのサブシステムイメージを App Store から直接インストールでき、慣れた開発やテスト環境を自由に構築できます。
    • Treeland ウィンドウコンポジター (プレビュー / 探索)
      • 独自開発したコンポジターは、アニメーションの滑らかさを 40% 向上させ、マルチフィンガータッチパッドジェスチャー追跡をサポートし、シルクのような滑らかな操作を実現します。
    • クロスデバイスコラボレーション
      • PC 間のキーボード/マウスとクリップボード共有をだけでなく、スマートフォンと PC 間の双方向制御を実現。
      • スマートフォンのメッセージに返信したり、スマートフォンのアプリをパソコンの大画面で操作したりすることで、シームレスで統合されたワークフローを構築可能。
    • 主要アーキテクチャ対応
      • AMD64 / ARM64 / LoongArch64 / RISC-V アーキテクチャに対応。
      • 対応デバイスには Raspberry Pi 4B+、Radxa Rock5B/5B+、ROCK 5 ITX マザーボードなどが含まれます。
    • グローバル対応
      • インストーラーに17言語を追加。
      • システムレベルの翻訳は51言語に対応。
      • App Store は地域別のコンテンツ配信をサポートし、グローバルユーザーに一貫したローカライズされた体験を提供。

deepin 25 の使用感や詳細な特徴などについては、少し調査する時間が必要です。今回は日本語用入力メソッドの設定方法を紹介するだけにとどめます。

日本語用入力メソッドの設定

日本語用入力メソッドを利用するにはパッケージのインストールと設定が必要になります。

パッケージのインストールには Debian と同じように apt コマンドを利用できます。ここでは、fcitx5-mozc をインストールして日本語用入力メソッドを利用できるようにする方法を紹介します。

bitwalk@deepin-PC:~$ sudo apt install fcitx5-mozc
[sudo] bitwalk のパスワード:
Verification successful
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています... 完了        
状態情報を読み取っています... 完了        
以下の追加パッケージがインストールされます:
  fcitx-mozc-data libprotobuf32 mozc-data mozc-server mozc-utils-gui
提案パッケージ:
  ibus-qt5
以下のパッケージが新たにインストールされます:
  fcitx-mozc-data fcitx5-mozc libprotobuf32 mozc-data mozc-server mozc-utils-gui
アップグレード: 0 個、新規インストール: 6 個、削除: 0 個、保留: 7 個。
13.8 MB のアーカイブを取得する必要があります。
この操作後に追加で 27.4 MB のディスク容量が消費されます。
続行しますか? [Y/n] y
取得:1 https://community-packages.deepin.com/beige crimson/main amd64 mozc-data all 2.28.4715.102+dfsg-2.3deepin1 [16.8 kB]
取得:2 https://community-packages.deepin.com/beige crimson/main amd64 fcitx-mozc-data all 2.28.4715.102+dfsg-2.3deepin1 [8,408 B]
取得:3 https://community-packages.deepin.com/beige crimson/main amd64 libprotobuf32 amd64 3.21.12-8deepin2 [941 kB]
取得:4 https://community-packages.deepin.com/beige crimson/main amd64 mozc-server amd64 2.28.4715.102+dfsg-2.3deepin1 [11.7 MB]
取得:5 https://community-packages.deepin.com/beige crimson/main amd64 fcitx5-mozc amd64 2.28.4715.102+dfsg-2.3deepin1 [276 kB]
取得:6 https://community-packages.deepin.com/beige crimson/main amd64 mozc-utils-gui amd64 2.28.4715.102+dfsg-2.3deepin1 [856 kB]
13.8 MB を 37秒 で取得しました (376 kB/s)                                                                    
以前に未選択のパッケージ mozc-data を選択しています。
...
(途中省略)
...
libprotobuf32:amd64 (3.21.12-8deepin2) を設定しています ...
mozc-data (2.28.4715.102+dfsg-2.3deepin1) を設定しています ...
fcitx-mozc-data (2.28.4715.102+dfsg-2.3deepin1) を設定しています ...
mozc-server (2.28.4715.102+dfsg-2.3deepin1) を設定しています ...
mozc-utils-gui (2.28.4715.102+dfsg-2.3deepin1) を設定しています ...
fcitx5-mozc:amd64 (2.28.4715.102+dfsg-2.3deepin1) を設定しています ...
desktop-file-utils (0.28-1) のトリガを処理しています ...
hicolor-icon-theme (0.17-2) のトリガを処理しています ...
libc-bin (2.38-6deepin13+rb1) のトリガを処理しています ...
bamfdaemon (0.5.6+repack-1) のトリガを処理しています ...
Rebuilding /usr/share/applications/bamf-2.index...
bitwalk@deepin-PC:~$ im-config -n fcitx5
bitwalk@deepin-PC:~$

コントロールセンターを起動して、左側の「デバイス」タブをクリック、右側画面の「キーボード」をクリックして表示される画面下側の「入力メソッド」をクリックした画面で、以下のように入力メソッドの追加をクリックして入力メソッドを追加します。

なお、システムを再起動した後に、入力メソッドの追加の一覧に Mozc が表示されました。

また、「キーボード - 英語(US)」は、日本語キーボードのレイアウトの場合は不要です。この例では、インストールした廉価な中華PCのキーボードが英語レイアウトだったので、英語のレイアウトも残しています。

日本語用入力メソッドを有効にするには Ctrl + スペース を押下します。

米国依存を減らしたい中国

華為 (Huawei) がスマートフォン向けに HarmonyOS を開発しているように、deepin は Windows に替わる OS を目指しているのでしょうか。

Wikipedia の Unity Operating System(UOS, 统一操作系统)の説明によると、一般ユーザー向けが deepin だとあります。UOS は明確に Windows 代替になることを目指しているプロジェクトです。

deepin の Community Edition は多言語に対応していますが、UOS のダウンロードサイトは英語の画面が提供されておらず解読が難しいです。しかし URL は英語表記 (www.chinauos.com/resource/download-professional) なので、こちらが Professional 版(商用版?)のダウンロードサイトであると推測できます。

例えば「统信UOS桌面专业版」は、UnionTech UOS Desktop Professional Edition です。

ちなみに statcounter で中国で使用されている OS のマーケットシェアを確認すると、依然 Windows のシェアが高いように見えます。それでも UOS が中国国内の Loongson などの CPU への対応を進めていることから、徐々にシェアは変化いくのかもしれません。

Source: StatCounter Global Stats - OS Market Share

参考サイト

  1. Deepin - Deepin Linux Distribution Wiki | DeepinWiki

 

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ オープンソースへ
にほんブログ村

オープンソース - ブログ村ハッシュタグ
#オープンソース



このエントリーをはてなブックマークに追加