こんな人にオススメ
雪の結晶ってフラクタル図形の一種でキレイなのは知ってるけど、これって実際にグラフで描ける?
ということで、今回は雪の結晶として有名なフラクタル図形の一種、コッホ曲線をplt
とplotly
を用いてグラフ化してみる。雪の結晶はかなり興味深い構造をしていて、端の方を拡大すると拡大前と同じ構造が現れる。
だから、騙し絵のような形でループ動画で無限に近づき続ける動画を作成することもできる。構造が同じだから動画の切れ目がわかりにくくなるからだ。
今回は完全に執筆者自身の疑問を解決するための記事になるが、キレイな図形なので是非とも色んなフラクタル図形を描いてくれたら幸いだ。
python環境は以下。
- Python 3.9.7
- numpy 1.21.2
- matplotlib 3.4.3
- plotly 5.3.1
- plotly-orca 3.4.2
作成したコード全文
下準備
import sys import numpy as np import matplotlib.pyplot as plt import plotly.graph_objects as go import plotly.io as pio sys.path.append('../../') import plt_layout_template import plotly_layout_template as template plt.ioff()
まずは下準備としてのimport
関連。plt_layout_template
はmatplotlib.pyplot
(plt
)の自作テンプレート。plotly_layout_template
はplotly
の自作テンプレー ト。これらを使用することで簡単にキレイなグラフを描ける。
-
-
【pltテンプレート】matplotlib.pyplotのグラフ作成テンプレート
続きを見る
-
-
【随時更新 備忘録】plotlyのグラフ即席作成コード
続きを見る
また、plt.ioff()
はVisual Studio Codeの拡張機能「Code Runner」でコードを実行する際に設定する内容。デフォルトでplt.ioff()
になっているが、自作テンプレートではplt.ion()
なので設定している。
これを設定しないとCode Runnerでplt
のグラフを表示することができない。グラフを保存するだけやエラーなく動作することを確認したいだけなら書く必要はない。
コッホ曲線のデータ作成
def _koch_snowflake_complex(order, scale): if order == 0: # 初期三角形 angles = np.array([0, 120, 240]) + 90 return scale / np.sqrt(3) * np.exp(np.deg2rad(angles) * 1j) else: ZR = 0.5 - 0.5j * np.sqrt(3) / 3 p1 = _koch_snowflake_complex(order - 1, scale) # 始点 p2 = np.roll(p1, shift=-1) # 終点 dp = p2 - p1 # 視点と終点をつなぐ new_points = np.empty(len(p1) * 4, dtype=np.complex128) new_points[::4] = p1 new_points[1::4] = p1 + dp / 3 new_points[2::4] = p1 + dp * ZR new_points[3::4] = p1 + dp / 3 * 2 return new_points
今回はフラクタル図形の1つであるコッホ曲線を使用するんだけど、これをグラフにするには少し骨が折れる。自分で計算してもよかったけど面倒だったのでplt
の「Filled polygon」にあったコードを参考にした。
このサイトではコッホ曲線をグラフにするためのコードとグラフ化の一例が載ってある。上のコードが実数と虚数を計算しているっぽい。そして以下のコードでこれをx
, y
に落としている。
def koch_snowflake(order, scale=2): """ フラクタル図形であるコッホ曲線を描くためのx, yを出力 Parameters ---------- order : int 曲線の次元 scale : float 曲線全体のサイズ """ points = _koch_snowflake_complex(order, scale) x, y = points.real, points.imag return x, y
このx
, y
をグラフにすることでコッホ曲線を描くことができる。
plt
でグラフ化
まずはplt
でグラフ化。plt
のサイトの方ではオーダーごとに1つのグラフを作成していたが、ここではsubplot
を使用して一気に複数グラフを描くことにした。こっちの方が全体を俯瞰しやすい。
-
-
【plt&subplot】pythonのpltで複数グラフを1つの画像に描く
続きを見る
画像だと伝わりにくいけど、order
が大きいグラフを実際に細部を拡大してみると同じ構造が続いているのがわかる。これがフラクタル図形。
# subplotの作成 fig, ax = plt.subplots(nrows=3, ncols=3, figsize=(10, 10)) # グラフレイアウトの調節 plt.subplots_adjust( left=0, # グラフ左を0とした時の左の余白 bottom=0.1, # グラフ左を0とした時の下の余白 right=1, # グラフ左を0とした時の右の余白 top=0.95, # グラフ左を0とした時の上の余白 wspace=0, # グラフの横の間隔 hspace=0.5, # グラフの縦の間隔 ) # axを展開して2次元から1次元に(3 x 3 = 9の配列) ax = ax.ravel() for order in range(9): x, y = koch_snowflake(order=order) ax[order].set_title(f"order: {order}") # x, yの最小値と最大値を設定 ax[order].set_xlim(-1.5, 1.5) ax[order].set_ylim(-1.5, 1.5) ax[order].fill(x, y) # 縦横比を一定に ax[order].set_aspect('equal') # グラフの保存と表示 fig.savefig('plt_koch_snowflake.png') plt.show()
plotly
でグラフ化するための関数たち
続いてはplotly
を使用してグラフ化してみるんだけど、plotly
でもsubplot
するのは面白くない。ということでplotly
ではスライダーを使用してグラフ化してみる。
-
-
【plotly&スライダー】plotlyのslidersにスライダーを追加
続きを見る
plotly
でグラフを描くとなると結構コードが長くなるので、関数に小分けしてグラフを作成することにする。関数を使用すると繰り返し使用したり修正が楽だったりする。
-
-
【plotly&工夫】楽にグラフを描くためのplotlyの関数化
続きを見る
プロットデータ作成用の関数
# プロットデータ作成用関数 def scatter(x, y, name): d = go.Scatter( x=x, y=y, name=name, mode='lines', fill='toself', visible=False, ) return d
まずはプロットデータ作成用の関数。今回は塗りつぶしを行うということでfill
を自分自身であるfill='toself'
に設定。また、スライダーで順次プロットを表示したいのでvisible
はFalse
にして初めは非表示にしている。
-
-
【plotly&fill】goで領域を塗りつぶし
続きを見る
スライダー設定用の関数
# スライダーの設定用関数 def slider(plot): # スライダーの各項目の設定 steps = [] for num, _ in enumerate(plot): visible = [False] * len(plot) # 全プロットを非表示 visible[num] = True # 該当プロットのみ表示 name = f"order: {num}" step = dict( method='update', # データとレイアウトを変更する label=num, # スライダー部分の文字 args=[ dict(visible=visible), # 表示・非表示の設定 dict(title=f"Koch snowflake ({name})") # グラフタイトル ] ) steps.append(step) # スライダーをグラフに設置するための設定 sliders = [ dict( active=0, # 初期表示 currentvalue_prefix="order: ", # スライダー上の表示の接頭辞 steps=steps, ) ] return sliders
スライダーの設定手順は以下。
- 全プロットを非表示に
- 各スライダー項目に該当するプロットを表示に変更
- 各スライダー項目のタイトルなどの設定をする
for文の「_
」は使用しないという慣例的な表現。for num in range(len(plot))
としてもいいけどなんか癪だったのでenumrate
を使った。どっちでもいい。
今回はプロットを表示・非表示にしたいのでデータをいじっているし、グラフタイトルも変更するのでレイアウトもいじっている。よってmethod
はデータとレイアウトを司どるupdate
にした。
また、steps
でスライダーの設定はできたが、これをグラフに設置しないといけない。その設定が変数sliders
になる。このsliders
をレイアウトに入れることで、グラフにスライダーを設定できる。
レイアウト作成用の関数
# レイアウト設定用の関数 def set_layout(sliders): layout = go.Layout( template=template.plotly_layout(), title="Koch snowflake (order: 0)", xaxis=dict(range=(-1.5, 1.5)), yaxis=dict( range=(-1.5, 1.5), scaleanchor="x", scaleratio=1, # グラフ比率をxと合わせる ), sliders=sliders, ) return layout
レイアウト作成で自作テンプレートを適用する。x
, y
軸の描画範囲を固定することで変に軸範囲のズレが起きないようにしている。また、scaleanchor, scaleratioを設定することでx, y軸の長さ比率を等しくしている。
先ほど作成したsliders
はここに入れてグラフに適用している。なお、ボタンを設置する際にもレイアウトでupdatemenus
という引数で設定可能。
-
-
【plotly&ボタン】plotlyのupdatemenusにbuttonsを追加
続きを見る
グラフ保存用の関数
# グラフ保存用の関数 def save(fig, config, save_name): pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca' pio.write_html(fig, f"{save_name}.html", config=config,) pio.write_image(fig, f"{save_name}.png")
最後はグラフ保存用の関数。保存形式はhtmlとpng。configをhtmlで設定することで、保存後のグラフでもツールバーを表示することができる。ツールバーでは図形の追加などが行える。
-
-
【plotly&config】グラフのツールバーを編集する
続きを見る
-
-
【plotly&orca】plotlyで静止画保存(orca)
続きを見る
plotly
のスライダーでグラフ化
ということで、plotly
のスライダーを使用してグラフを作成する。基本的には上で作成した関数を使用すればいいのでスッキリした内容で作成可能。
オーダーが大きいと記事に載せられないくらい重くなるので、オーダーは少し下げておいた。
スライダーを使用しているので、下のバーを左右に移動させることでコッホ曲線の次数を変更することができる。subplot
とは異なり一覧性はないけど、同じ位置で次数が増えていく様子がわかりやすい。
def graph(orders): plot = [] for order in range(orders): # コッホ曲線のx, yを計算 x, y = koch_snowflake(order=order) name = f"order: {order}" # プロットデータを作成、格納 d = scatter(x=x, y=y, name=name) plot.append(d) # 初期表示として初めのプロットのvisibleをTrueに変更 plot[0]['visible'] = True # スライダーの設定 sliders = slider(plot) # レイアウトの設定 layout = set_layout(sliders=sliders) # グラフの表示と保存 config = template.plotly_config() fig = go.Figure(data=plot, layout=layout) fig.show(config=config) save(fig=fig, config=config, save_name=f"plotly_koch_snowflake_{orders}") graph(orders=8)
自然現象をグラフ化するのはおもしろい
今回はフラクタル図形の1つであるコッホ曲線をplt
とplotly
を使用してグラフ化してみた。コッホ曲線のような不思議な性質を持つ図形を自分で描けるとどんどん改良したくなる。
コッホ曲線で言えば勝手にズームして無限ループできるようなアニメーションを加えてみても面白いかもしれない。世の中には不思議な図形が山のようにあるので、また気が向いたら作成したい。
関連記事
-
-
【plt&円グラフ】plt.pieでmatplotlibの円グラフを作成
続きを見る
-
-
【plotly&Scattergeo】世界地図に各国の首都の位置をプロット
続きを見る
-
-
【plotly&レーダーチャート】plotlyのRadar Chartの使い方とか設定とか
続きを見る