[Python][Gnuplot] ロジスティック写像の分岐図を描く
環境
Windows10 + Anaconda 4.8.1 + Python 3.7.6 + gnuplot 5.2(patchlevel 8)
はじめに
Pythonでロジスティック写像を計算し、Gnuplotを使って分岐図を描いてみた。Wikipediaの図ほど綺麗には描けなかったので気が向いたらリベンジしたい。
ロジスティック写像についてはwikiに詳しい説明がある。
ja.wikipedia.org
結果
ソースコード
実行には少し時間がかかる。自分の環境だと3分くらいかかった。
import subprocess import numpy as np from tqdm import tqdm def func(x, a): return a * x * (1 - x) def calc(*, a, x0, n, n_run): res = np.ones(n + n_run) * np.nan for i in range(n + n_run): if i == 0: res[i] = func(x=x0, a=a) else: res[i] = func(x=res[i - 1], a=a) return res[n_run:] N = 2000 N_run = 1000 x0 = 0.5 RESOLUTION = 6000 FILENAME = 'logistic_map.csv' def main(): a_list = np.linspace(2.4, 4.0, RESOLUTION) with open(FILENAME, mode='w') as f: for a in tqdm(a_list): xs = np.ones(N) * a ys = calc(a=a, x0=x0, n=N, n_run=N_run) for x, y in zip(xs, ys): f.write('{:f}, {:f}\n'.format(x, y)) cmd = '; '.join( [ r'set terminal png size 3840, 2880', r'set output \"logistic_map_gnuplot.png\"', r'set datafile separator \",\"', r'set xrange [2.4:4.0]', r'set yrange [0.0:1.0]', r'set noborder', r'set noxtics', r'set noytics', r'plot \"{}\" using 1:2 notitle pt 7 ps 0.1'.format(FILENAME), r'exit' ] ) cmd = 'gnuplot -e "{}"'.format(cmd) subprocess.run(cmd, text=True) return if __name__ == '__main__': main()
Gnuplotを使った他の記事
[Python][Gnuplot] PythonからGnuplotを使ってグモウスキー・ミラの写像を作図する
環境
Windows10 + Anaconda 4.8.1 + Python 3.7.6 + gnuplot 5.2(patchlevel 8)
はじめに
以前、PythonでMatplotlibを使ってグモウスキー・ミラの写像を作図したのだが、小さな点の打ち方が分からず、あまり綺麗な図にならなかった。
そのリベンジとして今回は、Matplotlibの代わりにGnuplotを使って作図してみる。
結果
ソースコード
import numpy as np from tqdm import tqdm import subprocess x0 = 0.1 y0 = 0.1 N = 1000000 FILENAME = 'gm.csv' def F(x, mu): return mu * x + 2 * (1 - mu) * x**2 / (1 + x**2) def func(x, y, alpha, sigma, mu): x_next = y + alpha * (1 - sigma * y**2) * y + F(x, mu) y_next = - x + F(x_next, mu) return (x_next, y_next) def calc(params): x, y = (x0, y0) with open(FILENAME, mode='w') as f: for _ in tqdm(list(range(N))): f.write('{:.8f}, {:.8f}\n'.format(x, y)) x, y = func(x, y, **params) return def plot(): cmd = '; '.join( [ r'set terminal png size 3840, 2880', r'set output \"test.png\"', r'set datafile separator \",\"', r'set xrange [-20.0:24.0]', r'set yrange [-14.5:12.5]', r'set noborder', r'set noxtics', r'set noytics', r'plot \"gm.csv\" using 1:2 notitle pt 7 ps 0.1', r'exit' ] ) cmd = 'gnuplot -e "{}"'.format(cmd) subprocess.run(cmd, text=True) return def main(): params = { 'mu': -0.8, 'alpha': 0.008, 'sigma': 0.05 } calc(params=params) plot() return if __name__ == '__main__': main()
解説
簡単にですが。
def calc():
~return
でグモウスキー・ミラの写像をN
回計算し、計算結果をgm.csv
(カンマ区切り)に出力。def plot():
~return
でGnuplotを使ってgm.csv
をプロット。- コマンドプロンプトで
gnuplot -e "command1;command2..."
と入力すると、gnuplotに一連のコマンドを実行させることができる。Pythonでからsubprocess.run
関数でこのコマンドを入力すればGnuplotを使うことができる(記事最下部の参考ページを参照のこと)。 - gnuplotに送るコマンドの中ではダブルクオーテーションをエスケープする必要があることに注意。
- 以下はgnuplotのコマンドの解説
set datafile separator ","
は、カンマ区切りのファイルを読み込むための設定。set noborder
で枠・軸を削除。set noxticks
とset noyticks
で目盛りを削除。plot "gm.csv" using 1:2 notitle pt 7 ps 0.1
でプロット。notitle
でlegendを削除。pt 7
でマーカーを丸に設定。ps 0.1
はマーカーサイズの設定。
参考ページ
PythonからGnuplotを使う方法を参考にさせていただいた。
auewe.hatenablog.com
[Python][Matplotlib] グモウスキー・ミラの写像を描く
はじめに
不思議な模様が現れることで有名な差分方程式系の一つに、グモウスキー・ミラの写像というものがある。
以前、PythonのMatplotlibを使って作図したコードを発見したので貼ってみる。本当はもっときれいに描きたかったのだが、Matplotlibでの方法がよく分からなかった。
出力結果
figure2の方は、五枚の翼を持つ鳥に見えるということで、写像の提案者の一人であるミラは「神話の鳥 (mythic bird)」と呼んだらしい。
ソースコード
import os.path import matplotlib.pyplot as plt import numpy as np from tqdm import tqdm x0 = 0.1 y0 = 0.1 T = 20000 N = 1000 marker = '.' markersize = 1 fillstyle = 'full' DPI = 1200 def F(x, mu): return mu * x + 2 * (1 - mu) * x**2 / (1 + x**2) def func(x, y, alpha, sigma, mu): x_next = y + alpha * (1 - sigma * y**2) * y + F(x, mu) y_next = - x + F(x_next, mu) return (x_next, y_next) def plot_times(*, x_init, y_init, ax, N, params): xs, ys = [np.ones(N) for _ in range(2)] xs[0], ys[0] = func(x_init, y_init, **params) for t in np.arange(N - 1): xs[t + 1], ys[t + 1] = func(xs[t], ys[t], **params) ax.plot( xs, ys, fillstyle=fillstyle, markerfacecolor='C0', markeredgecolor='C0', marker=marker, markersize=markersize, linewidth=0 ) return xs[-1], ys[-1] def plot(params, T, filename): x, y = (x0, y0) fig = plt.figure() ax = fig.add_subplot(111) ax.plot(x, y) times_now = 0 while True: if times_now >= T: break elif times_now + N < T: x, y = plot_times(x_init=x, y_init=y, N=N, ax=ax, params=params) times_now += N elif (times_now < T) and (times_now + N >= T): n = T - times_now x, y = plot_times(x_init=x, y_init=y, N=n, ax=ax, params=params) times_now += n else: raise ValueError fig.savefig(filename, dpi=DPI) return def main(): params = { 'mu': -0.38, 'alpha': 0.0083, 'sigma': 0.1 } plot(params=params, T=T, filename='figure1.jpg') params = { 'mu': -0.8, 'alpha': 0.008, 'sigma': 0.05 } plot(params=params, T=T, filename='figure2.jpg') return if __name__ == '__main__': main()
[Python][Matplotlib] 等高線プロットcontourfで一部に色を塗らない
結論から言えば、色を塗りたくない場所にnp.nan
を代入しておけばよい。
通常のcontourf
from itertools import product import matplotlib.pyplot as plt import numpy as np X = np.linspace(0, 5, 20) Y = np.linspace(0, 5, 20) X, Y = np.meshgrid(X, Y) Z0 = np.cos(1.5 * X) * np.cos(1.5 * Y) fig = plt.figure() ax = fig.add_subplot() cs = ax.contourf(X, Y, Z0) fig.colorbar(cs, ax=ax) fig.savefig('test_Z0.jpg', dpi=600) plt.close(fig)
一部領域で色を塗らないcontourf
例えば右下()で色を塗らない場合。
from itertools import product import matplotlib.pyplot as plt import numpy as np X = np.linspace(0, 5, 20) Y = np.linspace(0, 5, 20) Z0 = np.cos(1.5 * X) * np.cos(1.5 * Y) Z1 = np.copy(Z0) for yi, xi in product(range(Z1.shape[0]), range(Z1.shape[0])): if Y[yi] < X[xi]: Z1[yi][xi] = np.nan else: Z1[yi][xi] = np.cos(1.5 * X[xi]) * np.cos(1.5 * Y[yi]) fig = plt.figure() ax = fig.add_subplot() cs = ax.contourf(X, Y, Z1) fig.colorbar(cs, ax=ax) fig.savefig('test_Z1.jpg', dpi=600) plt.close(fig)
[Python][Matplotlib] よく使うリンク集
Matplotlibを使うときに自分がよく開くページのリンク集。公式ドキュメントがメインだが他のページも。随時追加。
目次
Matplotlibの公式ドキュメントについて
Matplotlibについて検索すると、バージョンの異なる複数のMatplotlib公式ドキュメントがヒットする。開いている公式ドキュメントが扱っているMatplotlibのバージョンは、URLを見るか、ヘッダー画像を見れば分かる(がやや分かりにくい)。
記事執筆時点での最新バージョンは3.2.2。
古いものでは、ver2.0のドキュメントも存在する。少なくともメジャーバージョンが同じドキュメントを参照するように注意した方がよいだろう。
最新バージョンは次のページで確認できる。
ちなみに自分が使用しているバージョンは__version__
変数で次で確認できる。
import matplotlib print(matplotlib.__version__)
あまり見ることはないが、リリース情報はこのページ。
色関連
Color
色の指定の仕方とか、Colormapクラスの説明とか
名前のついている色(文字列で指定可能な色)の一覧
公式ページではないが、色の指定方法について分かりやすくまとめられている。
等高線プロットContour(f)関連
imshow関連
[Python] CSVファイルの表をはてな記法の表に変換する
2020/07/16追記
オンラインコンバータを作ってみた。
cyanatlas.hatenablog.com
はじめに:はてな記法の表とは
はてなブログなどにははてな記法という独自の言語があり、これを使うことでHTMLファイルを編集せずにWEBページが書けます。
例えば次のように書けば表が表示されます。
|*名前|*色|*個数| |りんご|赤|1| |みかん|だいだい|2|
名前 | 色 | 個数 |
---|---|---|
りんご | 赤 | 1 |
みかん | だいだい | 2 |
問題点:はてな記法で表を書く面倒さ
はてな記法で表を書くのは、HTMLで表を書くより楽だが、それでもやや面倒であると感じる。理由は次の二つ。
- 編集中は列(縦のライン)が揃わないので、どこを編集しているのか分かりにくい
- WEBページの表をコピペできない
解決策としては次の二つが考えられる。
一つ目の方法については複数のWEBページで紹介されていたので各自検索してほしい。
一つ目の方法で解決できるとは言え、表組みのためだけに、表組以外は簡単に書けるはてな記法をやめたくないという私のような人もいるだろう。そこで、この記事は二つ目の方法を説明する。
解決策:CSVファイルをはてな記法に変換する(Python)
- Excelなどを使ってCSVファイルを作成する(エンコードUTF8-BOM、コンマ区切りとすれば、後に示すPythonコードをそのまま使えるはず)。
- 後に示すPythonコードで、FILENAME変数を変換したいCSVファイルの名前(あるいは相対パス)に変更する。
- Pythonコードを実行して、はてな記法を含むtxtファイルを出力する(ファイル名は
hatena_FILENAME.txt
)。
コードを示す。
import os import csv FILENAME = 'test.csv' def main(): res = '' with open(FILENAME, mode='r', encoding='utf-8-sig') as f: csvreader = csv.reader(f, delimiter=',') head = True for row in csvreader: for cell in row: if head: res += '|' + '*' + cell else: res += '|' + cell res += '|\n' head = False outputfilename = 'hatena_' + os.path.splitext(os.path.basename(FILENAME))[0] + '.txt' with open(outputfilename, mode='w', encoding='utf8') as f: f.write(res) return if __name__ == '__main__': main()
[Python] タプルには代入できないがタプル内リストには代入できるという話
環境:Windows10 + Python 3.7.6
リストとタプルの違い
Pythonの組み込み型には、配列を表現できるリストとタプルという二つの型が用意されている。この二つには、値の変更を許すかどうかに違いがある。
リスト:要素の変更を許す
タプル:要素の変更を許さない
リストの要素を変更することはできるが、タプルの要素を変更することはできない。もし無理に変更しようとすれば、エラーとなる。
コード 1
x = [1, 2, 3] print(x) x[0] = 9 print(x)
出力 1
[1, 2, 3] [9, 2, 3]
コード 2
x = (1, 2, 3) print(x) x[0] = 9 print(x)
出力 2
(1, 2, 3) Traceback (most recent call last): File "XXX.py", line 3, in <module> x[0] = 9 TypeError: 'tuple' object does not support item assignment
何度も変更する予定の場合はリストを使い、変更する予定がない場合はバグを防ぐためにタプルを使う。というのが基本的な方針である。
では、タプルの中身は変更されないと考えてよいだろうか?
実はそうとは限らない。以下に紹介するタプル内リストの要素を変更する場合、エラーが出ることなくタプルの中身を変更することができてしまう。
タプル内リストの落とし穴
次のコードではタプルの第一要素であるリストを変更しようとしているが、もちろんこれはエラーが出る。
コード 3
x = ([1, 1], [2, 2], [3, 3]) print(x) x[0] = [9, 9] print(x)
出力 3
([1, 1], [2, 2], [3, 3]) Traceback (most recent call last): File "XXX.py", line 3, in <module> x[0] = [9, 9] TypeError: 'tuple' object does not support item assignment
しかし、次のコードが示すように、タプル内リストの要素を変更することはできる。なぜなら、タプル側が参照しているリスト自体は変わっていないためである。タプルからすると、同じリストを参照していることは保証しているが、リストの中身が同じであることまでは保証してくれないのである。
コード 4
x = ([1, 1], [2, 2], [3, 3]) print(x) x[0][0] = 9 print(x)
出力 4
([1, 1], [2, 2], [3, 3]) ([9, 1], [2, 2], [3, 3])
解決策:タプル内リストを避ける
上で紹介したような奇妙な挙動は、バグを引き起こす原因となりやすい。そのため、プログラムの中ではタプル内リストを避けるのが良い。状況に応じてタプル内タプルかリスト内リストを使えば良いだろう。
変更予定のある変数 --> リスト内リストを使う
変更予定のない変数 --> タプル内タプルを使う