【matplotlib】グラフが更新されていくアニメーションを作りたい

Xを見ているとこんな感じでどんどんグラフが更新されていくアニメーションがある。これを作りたくなったのでやってみた。

 

トヨタの株価を取得してアニメーションにしてみました。

実行するにはimagemagickというソフトを入れる必要がありちょっと面倒臭いのですが、出来るようになると楽しいですね!これで色々な化学メーカーの比較をしてみたい。

 

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import yfinance as yf
import matplotlib.dates as mdates 

# トヨタの株価データを取得
toyota_ticker = 'TM'
toyota_data = yf.Ticker(toyota_ticker)

# 日次の株価データを取得し」月次へ変換
toyota_hist = toyota_data.history(start='1980-01-01', end='2023-03-25').resample('M').last()

#グラフの準備
fig, ax = plt.subplots(figsize=(10, 6))
plt.rcParams['font.family'] = 'Meiryo'#フォント指定

def animate(i):
    ax.clear()
    # 1フレームごとのグラフ
    #株価の推移(線)
    ax.plot(toyota_hist["Close"][:i+1], marker='None', linestyle='-', color='b')
    #最新の株価(点)
    ax.plot(toyota_hist[:i+1].index[-1], toyota_hist["Close"][:i+1].iloc[-1], marker='o', color='b', markersize=8, markerfacecolor="white")

    # グラフフォーマット
    ax.set_title('TOYOTA', fontsize=20)#グラフタイトル
    ax.set_ylabel('終値 (USD)', fontsize=20)#Y軸ラベル
    ax.grid(True)
    ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y'))#X軸ラベルを年表示に
    plt.yticks(fontsize=20)
    plt.xticks(ha="right",fontsize=20)
    plt.subplots_adjust(left=0.15) 

# アニメーションの設定
pause_frames = 20#アニメーションのループの前にポーズを入れる
ani = FuncAnimation(fig, animate, frames=len(toyota_hist)+ pause_frames, interval=1)

# アニメーションの保存
ani_file_path = "toyota_stock_prices_animation3.gif"
ani.save(ani_file_path, writer='imagemagick', dpi=50)

【matplotlib】グラフの正負によってグラデーションの色を変えたい【python】

この記事を見てたらプラス側とマイナス側で色を変えるというおしゃれなグラデーションをやっていたのでこれを勉強しておきたくなった。


stackoverflow.com

 

大雑把な解説はこちらを参照ください。

chemstat.hatenablog.com

 

まずはコード全体。

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(1)
x = np.linspace(0, 10, 200)
y = np.random.normal(0.01, 1, 200).cumsum()
               
fig, ax = plt.subplots(figsize=(12, 5))

ax.plot(x, y,color="black")

# 現在のy軸の範囲を取得
ylim = ax.get_ylim()

# 赤いグラデーションをプロットに追加
grad1 = ax.imshow(np.linspace(0, 1, 256).reshape(-1, 1),
                  cmap='OrRd',
                  vmin=-0.5,
                  aspect='auto',
                  extent=[x.min(), x.max(), 0, y.max()],
                  origin='lower')


# 青いグラデーションをプロットに追加
grad2 = ax.imshow(np.linspace(0, 1, 256).reshape(-1, 1),
                  cmap='PuBu',
                  vmin=-0.5,
                  aspect='auto',
                  extent=[x.min(), x.max(), y.min(), 0],
                  origin='upper')

#グラデーションを適用する領域を定義
poly_pos = ax.fill_between(x, y.min(), y, alpha=0.1)
poly_neg = ax.fill_between(x, y, y.max(), alpha=0.1)

# グラデーションを適用
grad1.set_clip_path(poly_pos.get_paths()[0], transform=ax.transData)
grad2.set_clip_path(poly_neg.get_paths()[0], transform=ax.transData)

# 不要になった塗りつぶし部分を削除
poly_pos.remove()
poly_neg.remove()

# y=0に黒い水平線を描画(基準線)
ax.axhline(0, color='black')

# y軸の範囲を元に戻す
ax.set_ylim(ylim)

# プロットを表示
plt.show()

 

グラデーションを貼り付ける

# 赤いグラデーションをプロットに追加
grad1 = ax.imshow(np.linspace(0, 1, 256).reshape(-1, 1),
                  cmap='OrRd',
                  vmin=-0.5,
                  aspect='auto',
                  extent=[x.min(), x.max(), 0, y.max()],
                  origin='lower')


# 青いグラデーションをプロットに追加
grad2 = ax.imshow(np.linspace(0, 1, 256).reshape(-1, 1),
                  cmap='PuBu',
                  vmin=-0.5,
                  aspect='auto',
                  extent=[x.min(), x.max(), y.min(), 0],
                  origin='upper')

 

グラデーションの領域を限定する

# yの値が正の場合にグラデーションを適用する領域を定義
poly_pos = ax.fill_between(x, y.min(), y, alpha=0.1)
poly_neg = ax.fill_between(x, y, y.max(), alpha=0.1)

# 正の領域にのみ青いグラデーションを適用
grad1.set_clip_path(poly_pos.get_paths()[0], transform=ax.transData)
grad2.set_clip_path(poly_neg.get_paths()[0], transform=ax.transData)

 

不要な部分を取り除く

# 不要になった塗りつぶし部分を削除
poly_pos.remove()
poly_neg.remove()


という事で完成しました。自由にグラデーションを割り付けられるのでデザインの自由度が広がっていいですね。

ちなみにコードの解説をChatGPTに解説してもらってるのですが、理解がはかどって素晴らしいです。

参考サイト

python - Gradient fill from zero till a curve - Stack Overflow

【matplotlib】グラデーションカラーマップの作成方法【python】

matplotlibにはcolormapが用意されているが、せっかくなので自分で作成したくなった。色をRGBA形式に変換すると足し算引き算が出来るので、それをプロットするといいらしい。

試すとこんな感じ。ちょっとした色の調整には便利です。赤→白→青みたいな変化をするものは出来ないので、その辺はデフォルトのものは便利ですね。

 

start_color = "#ffffff"

end_color =  "#000000"

 

start_color = "#eeeeee"

end_color = "#5555ff"

 

start_color = "#ffff55"

end_color = "#55ff55"



import
numpy as np import matplotlib.pyplot as plt import matplotlib.colors as mcolors # ユーザーによって指定されるパラメータ start_color = '#aaaaaa' # 初期色 end_color = '#333333' # 最終色 n_intervals = 10 # グラデーションの間隔 def create_custom_cmap(start_color, end_color, n_intervals): # グラデーションを生成 rgba_initial = np.array(mcolors.to_rgba(start_color))#初期色をRGBA形式に rgba_end = np.array(mcolors.to_rgba(end_color)) #最終色をRGBA形式に colors = [rgba_initial * (1 - i) + rgba_end * i for i in np.linspace(0, 1, n_intervals)]#初期色と最終色を混ぜる return mcolors.LinearSegmentedColormap.from_list("custom_cmap", colors) # カスタムカラーマップの作成 custom_cmap = create_custom_cmap(start_color, end_color, n_intervals) # カラーマップのデモンストレーション gradient = np.linspace(0, 1, 256) gradient = np.vstack((gradient, gradient)) plt.figure(figsize=(6, 2)) plt.imshow(gradient, aspect='auto', cmap=custom_cmap) plt.show()

【メモ】matplotlibに用意されているカラーマップ

chemstat.hatenablog.com

この記事でグラデーションのグラフ作成かっこいいなと思い始めたため、matplotlibに用意されているカラーマップを知りたくなりました。そんなわけでChatGPTに出力してもらいました。

いつでも調べれば見つかるのだけど、自分のブログに残しておく方が探しやすいので記事を作成。いずれ「あーあの時のカラーマップどこだっけ」とChatGPTに聞いたら答えてくれそうな気もするけれど。

 

*後日改めて調べたらもっといっぱいあったので追記。ChatGPTもまだまだですね。あと末尾に「_r」をつけるとcolormapの方向を逆に出来るという事が分かりました。

 

 

colormap逆順


【matplotlib】imshowとPathを使ってグラフをグラデーションで塗りつぶしたい【python】

この記事でグラフをグラデーションにするやり方を調べましたが、その時はfill_between関数を使って細切れに塗りつぶす方法でした。ただこのやり方だとx方向にしか色分けできず、もうちょっと自由に設定できるものはないかと調べたところimshowを使うパターンもあるようです。

chemstat.hatenablog.com

 

こまごまとした事はいったん置いておくとして、大雑把には以下のような流れで、背景をグラデーションにして、Pathの範囲内を切り出す、ということをやっています。

この辺は色々いじる方法がありそうなのでまたまとめようかと思います。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch

# データの作成
xx = np.arange(0, 11, 1)  # 0 から 10 までの整数を生成
yy = xx                   # xx と同じ値を yy に設定

# 最後尾の1点を追加
xx = np.append(xx, [10, 0])  # xx に [10, 0] を追加
yy = np.append(yy, [0, 0])   # yy に [0, 0] を追加

fig = plt.figure(figsize=(5, 5))  # 5x5 インチの図を作成
ax1 = fig.add_subplot(111)        # 1x1 グリッドの最初のサブプロットを作成

# パスの作成
path = Path(np.array([xx, yy]).transpose())  # xx と yy からパスを作成
patch = PathPatch(path, facecolor='none')    # パスからパッチを作成(塗りつぶしなし)
ax1.add_patch(patch)                         # サブプロットにパッチを追加

# 画像の表示
im = ax1.imshow(
    xx.reshape(yy.size, 1),                  # xx を2次元配列に変形して画像データとする
    cmap=plt.cm.Reds,                        # 赤色系のカラーマップを使用
    interpolation="bicubic",                 # バイキュービック補間で滑らかに表示
    origin='lower',                          # 画像の原点を下部に設定
    extent=[-5, 15, -5, 15],                 # 表示する軸の範囲を指定
    aspect="auto",                           # アスペクト比を自動に設定
    clip_path=patch,                         # パッチに沿って画像をクリップ
    clip_on=True                             # クリッピングを有効にする
)

# 軸の設定
ax1.set_xlim(-1, 11)  # x軸の表示範囲を -1 から 11 に設定
ax1.set_ylim(-1, 11)  # y軸の表示範囲を -1 から 11 に設定

fig.show()  # 図を表示

 

参考サイト

Filling between curves with color gradient or cmap in Matplotlib

【matplotlib】波長に応じて色を変えたグラデーションで塗りつぶしたい【python】

 

この記事でグラフの領域をグラデーションにする方法を調べたのですが、その応用としてUV-Visスペクトルを波長に応じた色で塗りつぶしたくなった。

chemstat.hatenablog.com

波長をRGBに変換する方法を調べたらこちらに欲しいコードがあったので拝借しました。

Coding Mess: Conversion of wavelength in nanometers to RGB in Python

xが380-780nmの時にRGBに変換して塗りつぶして、それ以外の範囲は黒色になります。実際に使うときはxとy1をスペクトルのデータに変換するといい感じになるかと。

 

import matplotlib.pyplot as plt
import numpy as np

#波長をRGBに変換するコード
#波長(380~780nm)を[R,G,B]にして返す。380~780nmから外れた場合は黒[255,255,255]を返す
#参考:https://codingmess.blogspot.com/2009/05/conversion-of-wavelength-in-nanometers.html
def wav2RGB(wavelength):
    w = int(wavelength)

    # colour
    if w >= 380 and w < 440:
        R = -(w - 440.) / (440. - 350.)
        G = 0.0
        B = 1.0
    elif w >= 440 and w < 490:
        R = 0.0
        G = (w - 440.) / (490. - 440.)
        B = 1.0
    elif w >= 490 and w < 510:
        R = 0.0
        G = 1.0
        B = -(w - 510.) / (510. - 490.)
    elif w >= 510 and w < 580:
        R = (w - 510.) / (580. - 510.)
        G = 1.0
        B = 0.0
    elif w >= 580 and w < 645:
        R = 1.0
        G = -(w - 645.) / (645. - 580.)
        B = 0.0
    elif w >= 645 and w <= 780:
        R = 1.0
        G = 0.0
        B = 0.0
    else:
        R = 0.0
        G = 0.0
        B = 0.0

    # intensity correction
    if w >= 380 and w < 420:
        SSS = 0.3 + 0.7*(w - 350) / (420 - 350)
    elif w >= 420 and w <= 700:
        SSS = 1.0
    elif w > 700 and w <= 780:
        SSS = 0.3 + 0.7*(780 - w) / (780 - 700)
    else:
        SSS = 0.0
    SSS *= 255

    return [int(SSS*R), int(SSS*G), int(SSS*B)]

##グラフの作成
fig, ax = plt.subplots()

#x,yの定義
#本来はここにスペクトルに対応するx,yを入れる
x = np.linspace(380, 780, 1000)
y1 = np.sin((x-200)/150)+1  #y1:sin関数
y2 = np.zeros_like(x)  #y2:0

#y1,y2,y3の線をプロット
ax.plot(x, y1, color='black')
ax.plot(x, y2, color='black')

#for文で塗りつぶす
for i in range(len(x) - 1):
    color = wav2RGB(x[i])#波長をRGBに変換
    ax.fill_between(x[i:i+2],#塗りつぶしを行うxの範囲
                    y1[i:i+2],#yの塗りつぶしの片側1
                    y2[i:i+2], #yの塗りつぶしの片側2
                    color=(color[0]/255,  color[1]/255,  color[2]/255, 0.5), #(R,G,B,透明度)
                    lw=0.5)

plt.show()

 

参考サイト

Coding Mess: Conversion of wavelength in nanometers to RGB in Python

【matplotlib】グラフの範囲をグラデーションで塗りつぶしたい【python】

以前グラフの線をグラデーションにしたい、という記事を書いた。今回は領域をグラデーションで塗りつぶしたくなった。

結局fill_betweenをfor文で繰り返して塗りつぶすのがいいらしい。ただこれだとx方向のグラデーションしかできないのでy方向や斜めはもうちょっと調べてみます

chemstat.hatenablog.com

 

 

import matplotlib.pyplot as plt
import numpy as np

##グラフの作成
fig, ax = plt.subplots()

#x,yの定義
x = np.linspace(0, 2 * np.pi, 1000)
y1 = np.sin(x)  #y1:sin関数
y2 = np.zeros_like(x)  #y2:0

#y1,y2,y3の線をプロット
ax.plot(x, y1, color='black')
ax.plot(x, y2, color='black')

#for文で塗りつぶす
for i in range(len(x) - 1):
    ax.fill_between(x[i:i+2],#塗りつぶしを行うxの範囲
                    y1[i:i+2],#yの塗りつぶしの片側1
                    y2[i:i+2], #yの塗りつぶしの片側2
                    color=(i/len(x),  i/len(x),  i/len(x), 1), #(R,G,B,透明度)
                    lw=1)

plt.show()

他の方法でやろうとしたものもあります。

chemstat.hatenablog.com