こんな人にオススメ
今までpythonのmatplotlib.pyplot
、plt
でグラフを作成してきたけど、plotly
ってどんな感じでグラフを描けるん?
plt
との比較で示してくれ!
っということで、plt
とplotly
で同じ内容のグラフを作成して比較する。つい先日、実際に同期にplotly
ってどうなん?と聞かれたのでここに記しておく。ちなみにその同期はこのブログの存在を知らない(はず)。
python環境は以下
- Python 3.9.4
- numpy 1.20.3
- matplotlib 3.4.2
- plotly 4.14.3
- plotly-orca 3.4.2
下準備
まずは下準備としてimport
関連。plt
については以下。plt.ion()
をすることで、ipython
などでインタラクティブモードにすることが可能。
執筆者は基本的にVScodeの「Code Runner」という拡張機能を使用しているのでion()
にしなくてもいいが、Code Runnerの場合はplt
のグラフを表示して確認することができないため一応記載。
Code RunnerなどのVScodeの拡張機能については以下参照。
import matplotlib.pyplot as plt import numpy as np plt.ion()
plotly
は以下。
import plotly.graph_objects as go import plotly.io as pio import numpy as np
続いて、横軸として使用するx
。x
はplt
、plotly
共通で0
から9
までの数値を1刻みで作成。縦軸についてはそれぞれのグラフで作成することとする。
x = np.arange(10)
データプロットと凡例
まずはシンプルに1つのプロットに対してグラフを作成する。プロットは2次関数でプロットの凡例は「plot1
」とした。ラベルはタイトル、横・縦軸、凡例につけた。
plt
y = x ** 2 plt.figure() # グラフの枠とかを表示 plt.title('plt1') # グラフタイトル plt.xlabel('x') # 横軸のラベル plt.ylabel('y') # 縦軸のラベル plt.plot(x, y, label='plot1') # データプロット。labelは凡例名 plt.legend() # 凡例を表示 plt.savefig('plt1') # グラフを画像として保存。デフォルトは.png
matplotlib
にはplt
を使用する方法と。fig
とかax
を使用する方法があるが今回は簡単なplt
で作成する。fig
とかax
の方が色々とグラフのカスタムができる。
タイトル、横軸ラベル、縦軸ラベルそれぞれはplt.title
, plt.xlabel
, plt.ylable
で指定する。また、プロットはplt.plot
で、x
, y
の順番に横軸、縦軸の値を指定する。そしてプロットの凡例名はlabel
引数で指定。今回は文字で凡例を作成したが数値も可能。
凡例を設定したが、このままでは設定しただけで表示することはできない。表示するためにplt.legend()
を使用。最後に作成したグラフをplt.savefig
で保存する。保存ファイル形式はデフォルトではpng
。pdf
などにも変換可能。
plotly
plot = [] # プロットデータを入れるためのlistを作成 d = go.Scatter(x=x, y=y, name='plot1') # nameでプロットの凡例を追加 plot.append(d) # プロットデータをlistに追加 # グラフのレイアウトをまとめて作成 layout = go.Layout( title=dict(text='plotly1'), # グラフタイトル xaxis=dict(title='x'), # 横軸のラベル yaxis=dict(title='y'), # 縦軸のラベル showlegend=True, # プロットが1本の時は凡例を表示するように指定 ) # 作成したプロットデータとレイアウトデータをまとめる fig = go.Figure(data=plot, layout=layout) fig.show() # グラフの表示 # グラフはHTML(後で動かせる)と画像(ここではpng。デフォルトはなさそう) pio.write_html(fig, 'plotly1.html') pio.write_image(fig, 'plotly1.png')
plotly
はグラフ構造が以下の2種類に分かれている。
- データ部分(プロットとか)
- レイアウト部分(軸ラベルとか)
ここでは変数plot
をlist
で定義し、これにプロットデータを入れることでデータ部分を作成。レイアウトに関してはlayout
変数で直接レイアウトの配列として作成している。
plotly
のベースとなるプロット方法がScatterなのでここでもScatterでグラフを作成している。x, yはそのままで、それぞれ横軸、縦軸の値を示す。name
が凡例に表示されるプロット名。
基本的にplotly
は辞書型なのでdict
で設定を指定している。グラフタイトルを示すtitle
引数に関しては文言をtext
で指定するが、x
, y
軸を示すxaxis
, yaxis
引数はtitle
で文言を決めることに注意。また、プロットが1本だけの場合は凡例が表示されないので表示するように設定。
作成したデータ部分とレイアウト部分をgo.Figure
で一つにしてfig.show()
でグラフを表示する。表示場所はブラウザが基本かと思っている。なのでグラフ作成時には自動でブラウザが起動する。
plotly
は作成したグラフをHTML方式で保存することも可能なので、pio.write_html
でHTMLで保存した。また、画像としてpngでも保存。HTMLのグラフは以下。
マーカーと線の種類、線の色を変更
続いてはプロットのマーカー(プロット点のこと)と線の種類、線の色を変更。ここでは丸いマーカー、黒の破線に設定した。
plt
y = x ** 2 plt.figure() plt.title('plt2') plt.xlabel('x') plt.ylabel('y') plt.plot( x, y, marker='o', # 円形のマーカーを追加 linestyle='--', # プロットは破線に変更 color='black', # プロットの色を黒に変更 label='plot2' ) plt.legend() plt.savefig('plt2')
plt1との変更点はプロット関係なので、plt.plotの引数に以下を追加。
marker
: マーカーlinestyle
: 線の種類color
: 線の色
マーカーについては丸以外にも四角や三角やバツなどのもある。線の種類も点線や一点鎖線もある。色も赤や緑などもある。
plotly
y = x ** 2 plot = [] # 設定は辞書型で行う d = go.Scatter( x=x, y=y, name='plot2', marker=dict(symbol='circle', size=10), # symbolがマーカーの役割 line=dict(dash='dash', color='black'), # 引数dashが線種。'dash'が破線 ) plot.append(d) layout = go.Layout( title=dict(text='plotly2'), xaxis=dict(title='x'), yaxis=dict(title='y'), showlegend=True, ) fig = go.Figure(data=plot, layout=layout) fig.show() pio.write_html(fig, 'plotly2.html') pio.write_image(fig, 'plotly2.png')
先ほど同様、plotlyは辞書型で設定を変更するのでdictを使用。マーカーと線について大別し、それぞれ以下のように対応。
marker
: マーカー関係symbol
: プロット点。circle
は円形
line
: 線関係dash
: 線の種類。dash
は破線color
: 線の色。black
は黒
基本的にはplt
と同じようにさまざまなマーカーや線の種類を使用することが可能だが、指定方法が異なる点もあるので注意。plotlyのマーカーについては以下参照。
-
-
【plotly&マーカー】plotlyのマーカーのシンボル
続きを見る
複数のプロットを同時に描画
続いては、1つのグラフに複数のプロットを追加するというもの。同時に描画することで比較しやすくなる。プロットは先ほど同様2次関数を使用し、それぞれ+10
ずつしてy
方向に平行移動させた。また、プロット作成にはfor
ループを使用した。
for
ループなど、基礎的なpythonの挙動については以下参照。
-
-
【python&初級】のlistとかforとかifとかまとめ
続きを見る
plt
# figureはグラフの枠なので、figureより上でプロットすると別々のグラフになる plt.figure() plt.title('plt3') plt.xlabel('x') plt.ylabel('y') for num in range(5): y = x ** 2 + (10 * num) plt.plot(x, y, label=f"plot{num}") plt.legend() plt.savefig('plt3')
複数プロットする際にはplt.figure()
をプロットより前に持ってこないとウィンドウが複数個できてしまう。これについては今までのグラフも同様。
複数プロットをすると色は自動で変わり、凡例はデフォルトでは縦に並ぶようになっている。
plotly
plot = [] for num in range(5): y = x ** 2 + (10 * num) d = go.Scatter(x=x, y=y, name=f"plot{num}",) plot.append(d) layout = go.Layout( title=dict(text='plotly3'), xaxis=dict(title='x'), yaxis=dict(title='y'), ) fig = go.Figure(data=plot, layout=layout) fig.show() pio.write_html(fig, 'plotly3.html') pio.write_image(fig, 'plotly3.png')
plotly
の場合はデータ部分をまとめる変数plot
を用意していたので、plot
にデータ部分を入れていくことで複数プロットを実現することが可能。
複数のプロットでマーカーと線の種類を変更
複数プロットをしたグラフを作成したので、マーカーと線の種類を変更して表示してみる。
plt
# 丸、四角、三角、バツ、α marker = ['o', 's', '^', 'x', r'$alpha$'] # 実線、破線、点線、線なし、一点鎖線 linestyle = ['-', '--', ':', ' ', '-.'] plt.figure() plt.title('plt4') plt.xlabel('x') plt.ylabel('y') for num in range(len(linestyle)): m = marker[num] # 予めマーカーを変数として新たに定義し見やすく ls = linestyle[num] # 予め線種を変数として新たに定義し見やすく y = x ** 2 + (10 * num) plt.plot( x, y, marker=m, linestyle=ls, label=f"'{m}' , '{ls}'", # 「'マーカー', '線種'」で凡例を作成 ) plt.legend() plt.savefig('plt4')
ここで使用するマーカーは丸、四角、三角、×、そしてギリシャ文字のα。αについては$\LaTeX$で書くことで使用することができる。
線の種類に関しては実線、破線、点線、線なし、一点鎖線を使用。なお、線の種類に関しは以下のような対応関係がある。どちらを使用しても良い。
-
(実線):solid
—
(破線):dashed
:
(点線):dotted
-.
(一点鎖線):dashdot
plotly
# 丸、四角、三角、バツ、星みたいなやつ symbol = ['circle', 'square', 'triangle-up', 'x', 'hexagram'] # 実線、破線、点線、自作一点鎖線、一点鎖線 linestyle = ['solid', 'dash', 'dot', '10px, 20px, 30px, 5px', 'dashdot'] plot = [] for num in range(len(linestyle)): sy = symbol[num] # 予めマーカーを変数として新たに定義し見やすく ls = linestyle[num] # 予め線種を変数として新たに定義し見やすく y = x ** 2 + (10 * num) d = go.Scatter( x=x, y=y, name=f"'{sy}' , '{ls}'", # 「'マーカー', '線種'」で凡例を作成 marker=dict(symbol=symbol[num], size=10), line=dict(dash=linestyle[num]), ) plot.append(d) layout = go.Layout( title=dict(text='plotly4'), xaxis=dict(title='x'), yaxis=dict(title='y'), ) fig = go.Figure(data=plot, layout=layout) fig.show() pio.write_html(fig, 'plotly4.html') pio.write_image(fig, 'plotly4.png')
plotlyでは$\LaTeX$でのマーカーを作成できなかったので代わりに星みたいなバッジのようなマーカーhexagram
を使用。また、線の種類も線なしが選択できなかったので自作の一点鎖線を使用。この自作の一点鎖線は以下のような法則で線を形作っているはず。
- 10 pixelだけ実線
- 20 pixelだけ空白
- 30 pixelだけ実線
- 5 pixelだけ空白
- 上記4項目をループごとに実線・空白を入れ替えながら実行
マーカーについては、今回使用したもの以外にも多くの種類が存在している。一覧にしたものについては以下参照。
-
-
【plotly&マーカー】plotlyのマーカーのシンボル
続きを見る
軸の表示範囲の指定とグリッドの追加
パッとグラフを見たときに内容がわかりやすい方が考えることが少なくて済む。ということでここでは軸の表示範囲の指定と横・縦軸にそれぞれ垂直・平行なグリッドを作成する。
plt
y = x ** 2 plt.figure() plt.title('plt5') plt.xlabel('x') plt.ylabel('y') plt.xlim(-1, 10) # 横軸の表示範囲 plt.ylim(-10, 100) # 縦の表示範囲 # グリッドの追加 # axisで横・縦軸どちらに入れるか、colorで色を指定 plt.grid(axis='x', color='red') plt.grid(axis='y', color='green') plt.plot(x, y, label='plot1') plt.legend() plt.savefig('plt5')
横軸の表示範囲指定についてはplt.xlim((最小値)、(最大値))
で指定する。また、グリッドについてはplt.grid
で作成し、axis
で軸を選択することが可能。
上のコードでは横軸・縦軸バラバラで設定しているがaxis='both'
にすると両方の設定を同時に可能。ただし、この場合はバラバラに設定していないので、横・縦軸ともに同じ内容が設定される。
plotly
y = x ** 2 plot = [] d = go.Scatter(x=x, y=y, name='plot1') plot.append(d) # plotlyはグリッドが初めから入っているので色だけ変更 layout = go.Layout( title=dict(text='plotly5'), xaxis=dict( title='x', range=(-1, 10), # 横軸の範囲 gridcolor='red', # グリッドの色をredに ), yaxis=dict( title='y', range=(-10, 100), # 縦軸の範囲 gridcolor='green', # グリッドの色をgreenに zeroline=False # デフォルトでは0で白の線画入っている削除 ), showlegend=True, ) fig = go.Figure(data=plot, layout=layout) fig.show() pio.write_html(fig, 'plotly5.html') pio.write_image(fig, 'plotly5.png')
plotly
では表示範囲だけではなく、グリッドに関してもxaxis
, yaxis
でそれぞれ設定する必要がある。表示範囲はrange
引数で行う。一方でグリッドに関してはデフォルトで入っているので、ここではgridcolor
引数でグリッドの色の変更のみを行った。
ここでの注意点はplotly
はデフォルトでは横・縦軸それぞれの0
の値に白の線が入っているという点。これはグリッドの設定では変更できず、別途zeroline
引数での設定が必要になる。ここでは縦軸に対してのみzeroline
をFalse
にして0
の線を消している。
フォント自体とフォントサイズの変更
これまでに上で作成したグラフは自分のPC上で表示する分には特に問題がないように思われる。しかしスライドなどで他者に見せるときには文字の大きさに気をつけなければならない。
他者に見せるということは自分主体ではなく相手主体。すなわち見やすさがとても重要。ということでここではフォントとそのサイズを変更してみる。
plt
# 凡例のフォントを変更するためにモジュールをimport import matplotlib.font_manager as font_manager y = x ** 2 # フォントはfont引数で指定可能 # フォントサイズは各項目でfontsize引数で指定可能 # 先にフォントを指定しないとフォントサイズの変更が反映されない plt.figure() # タイトルはフォントサイズの変更が反映されていない plt.title('plt6', fontsize=100, font='Times New Roman') plt.xlabel('x', font='sans serif', fontsize=40) plt.ylabel('y', font='Arial', fontsize=20) plt.xticks(font='sans serif', fontsize=10) # 横軸の目盛の設定 plt.yticks(font='Arial', fontsize=20) # 縦軸の目盛の設定 plt.plot(x, y, label='plot1') # 凡例のフォントサイズだけなら直接指定可能 # フォントをいじるならprop引数の中で指定しないとフォントサイズの変更が反映されない # fontは凡例のフォントの情報を入れる配列 font = font_manager.FontProperties( family='Comic Sans MS', size=30 ) # plt.legend(fontsize=40) # フォントサイズの変更だけならこれでOK plt.legend(prop=font) plt.savefig('plt6')
フォント自体はfont
引数でフォントの名称を設定可能。フォントサイズはfontsize
引数で可能。ここでの注意点はfont
引数よりもfontsize
引数を先に書かないと反映されないということ。上の例ではタイトルのフォントサイズを驚異の100
に設定したが反映されていない。それ以外の軸ラベルと目盛、凡例については反映されている。
また、フォントサイズが大きすぎるとグラフからはみ出てしまうので注意が必要。
凡例についてはこれらとは設定方法が異なり、フォントサイズの変更だけならそのままfontsize=30
のように書けばいいが、フォント自体も変更するなら別途font_manager
モジュールが必要になる。
font_manager.FontProperties
でフォント自体とフォントサイズを設定することができるが、フォントについてはfamily
引数として指定しないといけないのでここも注意。
plotly
y = x ** 2 plot = [] # プロットデータを入れるためのlistを作成 d = go.Scatter( x=x, y=y, name='plot1', # ホバー時のフォントについて各プロットで指定するならここに書く # hoverlabel=dict(font=dict(family='Times New Roman', size=30)), ) plot.append(d) layout = go.Layout( title=dict( text='plotly6', font=dict(size=100, family='Times New Roman'), ), xaxis=dict( title=dict( text='x', font=dict(size=40, family='sans-serif') ), tickfont=dict(family='Times New Roman', size=40), ), yaxis=dict( title=dict( text='y', font=dict(size=20, family='Arial') ), tickfont=dict(family='sans-serif', size=20), ), showlegend=True, legend=dict(font=dict(family='Comic-Sans-MS', size=30)), # 全プロットのホバーのフォント系の一括変更はレイアウトで指定 hoverlabel=dict( font=dict(family='cursive', size=30) ), ) fig = go.Figure(data=plot, layout=layout) fig.show() pio.write_html(fig, 'plotly6.html') pio.write_image(fig, 'plotly6.png')
フォント自体とフォントサイズの変更に関してはplt
の時と同様に設定可能。ただ、dict
形式になっているので見づらくはなっている。plotly
の場合もフォントが大きすぎるとグラフからはみ出てしまうが、フォント自体とフォントサイズの順番は気にしなくてもいい。凡例のフォントについてはレイアウトのlegend
引数で行うことが可能。
これまでのplotly
のプロットのHTMLに触れた人はわかるかもしれないが、plotly
ではプロット点付近にカーソルを持っていくと自動でその情報が出てくるようになる。このフォントなども変更可能。
それぞれのプロットでホバーの設定を変更する場合はScatter
の中に書くが、統一する場合はレイアウトの中に書くことで統一的に記述することが可能。実はこれ知らなかった。毎回Scatter
の中に書いてた。マジか。
テンプレート(デフォルト)の使用
実は上記のグラフでもまだまだキレイにできる部分はある。例えば以下。
- 目盛は内向き
- 右、上にも目盛を振る
- 全フォントは
Times New Roman
- グラフ背景は無色透明
これらも加えてグラフを書くとなると相当の行が必要になる。ということでテンプレート(デフォルト)を作成することで自動で設定がキレイになるようにする。
plt
# テンプレートは辞書型にしておくと対応関係と使いやすさでおすすめ change_dct = { 'font.size': 20, 'font.family': 'Times New Roman', 'mathtext.fontset': 'stix', "figure.figsize": [16, 8], 'figure.facecolor': (0, 0, 0, 0), 'axes.facecolor': (0, 0, 0, 0), 'xtick.direction': 'in', 'ytick.direction': 'in', "xtick.top": True, "ytick.right": True, 'xtick.minor.visible': True, 'ytick.minor.visible': True, 'legend.edgecolor': 'black', 'legend.facecolor': 'w', } # テンプレートをループで回しながら今のテンプレートに上書き for key, value in change_dct.items(): plt.rcParams[key] = value y = x ** 2 plt.figure() plt.title('plt7') plt.xlabel('x') plt.ylabel('y') plt.plot(x, y, label='plot1') plt.legend() plt.savefig('plt7')
plt
の場合はplt.rcParams
に出力時点での設定一覧を表示することが可能。plt.rcParams
は辞書型なので、変更したいテンプレートを予め辞書型で作成しておくとわかりやすい。作成したテンプレート辞書をループで回しながら既存のテンプレートに上書きすることで、上書き後のグラフは全てテンプレートのグラフの設定となる。
注意点は一回テンプレートを上書きしてしまうとそれ以降はその情報が保持されるということ。戻したかったら再度上書きするか、ipython
を抜けるとかしてpython環境をexit
しないといけない。
plt
のテンプレートについて詳しいことは以下参照。
-
-
【pltテンプレート】matplotlib.pyplotのグラフ作成テンプレート
続きを見る
change_dct
のそれぞれの引数については以下。なお。(0, 0, 0, 0)
は(Red, Green, Blue. alpha)
を表しており、Red
, Green
, Blue
が0
なら黒、1
なら白、そしてalpha
が0
なら透明となる。
ちなみにグラフ・プロットの設定はplt1
と同じなので、テンプレートをいじるだけで随分と様変わりしていることがわかる。
plotly
# テンプレートはgo.Layoutと同じ形式で書く layout_template = dict( layout=go.Layout( width=1280 * 1.3, height=720 * 1.3, title=dict( x=0., y=0.99, xanchor='left', yanchor='top', xref='paper', yref='container', ), xaxis=dict( ticks='inside', zeroline=False, linewidth=1, showline=True, linecolor='black', mirror='allticks', ), yaxis=dict( ticks='inside', zeroline=False, linewidth=1, showline=True, linecolor='black', mirror='allticks', ), yaxis2=dict( gridcolor='#d5d5d5', ticks='inside', zeroline=False, linewidth=1, showline=True, linecolor='black', mirror=False, overlaying='y', side='right', ), font=dict(family='Times New Roman', color='black', size=25), paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)', legend=dict( x=1, y=1, orientation='v', xanchor='left', yanchor='top', itemclick='toggleothers', itemdoubleclick='toggle', bgcolor='rgba(0,0,0,0)', bordercolor='black', borderwidth=1, ), hovermode='closest', showlegend=True, hoverdistance=30, ) ) y = x ** 2 plot = [] d = go.Scatter(x=x, y=y, name='plot1') plot.append(d) layout = go.Layout( template=layout_template, # テンプレートの読み込み title=dict(text='plotly7'), xaxis=dict(title='x'), yaxis=dict(title='y'), showlegend=True, ) fig = go.Figure(data=plot, layout=layout) fig.show() pio.write_html(fig, 'plotly7.html') pio.write_image(fig, 'plotly7.png')
plotly
の場合は通常のレイアウトと同じ書き方でテンプレートを作成することができる。項目が多いので省略しながら以下に設定の軽い説明をする。
plotly
のテンプレートについて詳しいことは以下参照。
-
-
【plotlyテンプレート】plotlyのグラフ作成テンプレート
続きを見る
-
-
【随時更新 備忘録】plotlyのグラフ即席作成コード
続きを見る
plotly
もplt
と同様、plotly1
のグラフを使用しているが、かなり見た目が変わっていることがわかる。ちなみにplotly
ではconfig
という設定も可能。config
については以下参照。
-
-
【plotly&config】グラフのツールバーを編集する
続きを見る
手札をたくさん持て
今回はplt
とplotly
のグラフを比較した。plotly
の方が文章量的には多いけど、その分できることが多い。何ができるかは色々と記事を書いているので、本ブログ「M天パ」なりご自身で調べるなりしていただければ幸いだ。
pltしか使えない状態よりも確実にplotly
まで使えた方が武器にある。自分に合ったもの、その時々に合ったものを選択できる自由があるということはより最適な成果物を創ることができるということ。
執筆者自身、まだまだ知らないことが多いのでこれからも手札をたくさん持てるようにする。
関連記事
-
-
【plt&pandas】df.plot()でmatplotlibのグラフを作成
2022/8/19
こんな人にオススメ pandasのデータフ& ...
-
-
【plotly&fig作成と更新】add_traceやupdate_layoutの使い方
2022/8/19
こんな人にオススメ plotlyでグラフを& ...
-
-
【plotly&size, width】Scatterのサイズやlineの太さ一覧表を作成
2022/8/19
こんな人にオススメ plotlyのScatterのマー ...
-
-
【plotly&legendまとめ】凡例の引数一覧
2022/8/19
こんな人にオススメ plotlyのlayoutの引数leg ...