カテゴリー

当サイトはアフィリエイトプログラムによる収益を得ています〈景品表示法に基づく表記です)

Pythonの発展系

【plotly&コッホ曲線】フラクタル図形の雪の結晶をスライダーで作成してみる

2021年10月3日

こんな人にオススメ

雪の結晶ってフラクタル図形の一種でキレイなのは知ってるけど、これって実際にグラフで描ける?

ということで、今回は雪の結晶として有名なフラクタル図形の一種、コッホ曲線をpltplotlyを用いてグラフ化してみる。雪の結晶はかなり興味深い構造をしていて、端の方を拡大すると拡大前と同じ構造が現れる。

だから、騙し絵のような形でループ動画で無限に近づき続ける動画を作成することもできる。構造が同じだから動画の切れ目がわかりにくくなるからだ。

今回は完全に執筆者自身の疑問を解決するための記事になるが、キレイな図形なので是非とも色んなフラクタル図形を描いてくれたら幸いだ。

python環境は以下。

  • Python 3.9.7
  • numpy 1.21.2
  • matplotlib 3.4.3
  • plotly 5.3.1
  • plotly-orca 3.4.2

運営者のメガネです。YouTubeTwitterInstagram、自己紹介はこちら、お問い合わせはこちらから。

運営者メガネ

作成したコード全文

下準備

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_templatematplotlib.pyplotplt)の自作テンプレート。plotly_layout_templateplotlyの自作テンプレー ト。これらを使用することで簡単にキレイなグラフを描ける。

plt.rcParamsでデフォルトを変更した後のグラフ
【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'に設定。また、スライダーで順次プロットを表示したいのでvisibleFalseにして初めは非表示にしている。

【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

スライダーの設定手順は以下。

  1. 全プロットを非表示に
  2. 各スライダー項目に該当するプロットを表示に変更
  3. 各スライダー項目のタイトルなどの設定をする

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でグラフを静止画として保存する際に出るポップアップ
【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つであるコッホ曲線をpltplotlyを使用してグラフ化してみた。コッホ曲線のような不思議な性質を持つ図形を自分で描けるとどんどん改良したくなる。

コッホ曲線で言えば勝手にズームして無限ループできるようなアニメーションを加えてみても面白いかもしれない。世の中には不思議な図形が山のようにあるので、また気が向いたら作成したい。

関連記事

【plt&円グラフ】plt.pieでmatplotlibの円グラフを作成

続きを見る

【plotly&Scattergeo】世界地図に各国の首都の位置をプロット

続きを見る

【plotly&レーダーチャート】plotlyのRadar Chartの使い方とか設定とか

続きを見る

ガジェット

2023/9/18

【デスクツアー2022下半期】モノは少なく、でも効率的に Desk Updating #0

今回はガジェットブロガーなのにデスク環境を構築していない執筆者の ...

ライフハック

2023/9/16

【Audible vs YouTube Premium】耳で聴く音声学習コンテンツを比較

ワイヤレスイヤホンが普及し耳で学習することへのハードルが格段に下 ...

完全ワイヤレスイヤホン(TWS)

2023/9/18

【SENNHEISER MOMENTUM True Wireless 3レビュー】全てが整ったイヤホン

今回は高音質・高機能なSENNHEISERのフラグシップ完全ワイヤレスイヤホン「SENNH ...

ライフハック

2023/3/11

【YouTube Premiumとは】メリットしかないから全員入れ

今回はYouTube Premiumを実際に使ってみてどうなのか、どんなメリット/デメリット ...

マウス

2023/8/17

【Logicool MX ERGOレビュー】疲れない作業効率重視トラックボールマウス

こんな人におすすめ トラックボールマウスの王道Logicool MX ERGOが気になるけどऩ ...

ベストバイ

2023/9/18

【ベストバイ2022】今年買って良かったモノのトップ10

2022年ベストバイ この1年を振り返って執筆者は何を買ったのか。ガジェッ& ...

スマホ

2023/1/15

【楽天モバイル×povo2.0の併用】月1,000円の保険付きデュアルSIM運用

こんな人におすすめ 楽天モバイルとpovo2.0のデュアルSIM運用って実際のとこ ...

マウス

2023/9/16

【Logicool MX ERGO vs MX Master 3】ERGOをメインにした決定的な理由

こんな疑問・お悩みを持っている人におすすめ 執筆者はLogicoolのハイエンӠ ...

macOSアプリケーション

2022/9/30

【Chrome拡張機能】便利で効率的に作業できるおすすめの拡張機能を18個紹介する

こんな人におすすめ Chromeの拡張機能を入れたいけど、調べても同じような ...

macOSアプリケーション

2023/5/3

【Automator活用術】Macで生産性を上げる作業の自動化術

今回はMacに標準でインストールされているアプリ「Automator」を使ってできる ...

Pythonを学びたいけど独学できる時間なんてない人へのすゝめ

執筆者は大学の研究室・大学院にて独学でPythonを習得した。

でも社会人になったら独学で行うには時間も体力もなくて大変だ。

時間がない社会人だからこそプロの教えを乞うのが効率的。

ここでは色んなタイプに合ったプログラミングスクールの紹介をする。

  • この記事を書いた人

メガネ

Webエンジニア駆け出し。独学のPythonで天文学系の大学院を修了。常時金欠のガジェット好きでM2 Pro MacBook Pro(30万円) x Galaxy S22 Ultra(17万円)使いの狂人。自己紹介と半生→変わって楽しいの繰り返しレビュー依頼など→お問い合わせ運営者情報、TwitterX@m_ten_pa、 YouTube@megatenpa、 Threads@megatenpa

-Pythonの発展系
-, , ,