====== matplotlib ======
matplotlibはpythonでグラフを描画できるパッケージである.\\
* ''matplotlib.pyplot''モジュールを利用するのが一般的.この場合は import matplotlib.pyplot as pltでimportする.
* 私の推奨は Fiture を使うもの。 from matplotlib.figure import Figure
pyplotモジュールを直接操作して手続き的に図を作成していく方法もあるが,
内部にあるFigureクラスを用いてオブジェクト指向的に操作する方法もある.
ネット上の解説では手続き的に作成していく方法が多いが,
ここではオブジェクト指向でやっていく.
=== オブジェクト指向操作概要 ===
pyplotモジュールには ''figure()'' 関数があり,
これにより新規の ''matplotlib.figure.Figure'' クラスのインスタンスが得られる.
Figureクラスは作成するグラフの紙面のようなもの.Figureを分割して中に複数の図を入れることもできる.
Figureクラスは ''add_subplot()'' メソッドを持ち,
これによって ''matplotlib.axes._subplots.AxesSubplot'' クラスのインスタンスが得られる.
AxesSubplotクラスはpyplotモジュールの持つ関数を普通に使える\\
(全てが使えるわけではない 例 ''.xlabel()'' -> ''.set_xlabel()'' )\\
のでpyplotの代わりとして使っていくのが良いだろう.
オブジェクト指向でやっていくならFigureインスタンスにAxesSubplotを用意して使う.
=== まとめ ===
* pltモジュール
* Figureクラス : savefig()メソッドで画像として保存.add_subplot()メソッドでAxesSubplotインスタンスの呼び出し.
* AxesSubplotクラス : pltモジュールと同じ関数がメソッドとして使える.
=== 追記 ===
Jupyter で正しくオブジェクト指向をやっていくなら plt 自体 import してはいけない。
''matplotlib.figure'' の ''Figure'' をimport してやっていくのが良い。\\
''plt'' をimport しなかったら ''plt.close()'' できない気がするが大丈夫か?
-> 大丈夫なのかも
Colab のときは最初から ''matplotlib inline'' されているから問題ないが、
jupyter のときは ''matplotlib inline'' を手動でやっていく必要がある。
=== add_subplot() ===
''Figure.add_subplot(arg1,arg2,arg3)'' でSubplotを作成するときの
引数について解説する.
引数は前から順に
* (arg1) 行の個数
* (arg2) 列の個数
* (arg3) 場所
を表す.
場所は1から始まり,1行1列,1行2列,...の順番に数えていく.(下図参照)
^ ^ 列 ^^^
^ 行 | 1 | 2 | 3 |
^::: | 4 | 5 | 6 |
=== 例 ===
fig = plt.figure()
sbplt = fig.add_subplot()
sbplt.scatter(x=X[:,0],y=X[:,1],c=colors)
fig.savefig(output_path) # または sbplt.figure.savefig(output_path)
plt.close() # 20枚以上使う場合は必ずcloseが必要.
fig, sbplts = plt.subplots(rows,cols) # rows,cols が共に1のとき sbplts がlistじゃなくなるので注意
sbplts[i].scatter(x=X[:,0],y=X[:,1],c=colors)
fig.savefig(output_path) # または sbplt.figure.savefig(output_path)
plt.close() # 20枚以上使う場合は必ずcloseが必要.
ndarray に書き出す
import io
with io.BytesIO() as buff:
fig.savefig(buff, format='png', dpi=dpi)
figarray = cv2.decode( np.frombuffer(buff.getvalue(),dtype=np.uint8), 1)
=== 3次元グラフを書く ===
* {{https://qiita.com/orange_u/items/8a1e285a45093857aef7}}
==== 対応表 ====
^ pylot手続きインターフェース ^ オブジェクト指向インターフェース ^ 意味 ^
| ''plt.figure( figsize=(8.0,8.0) )'' | ''Figure( figsize=(8.0,8.0) )'' | 図のサイズを決める.(横,縦) 100倍. |
^ ^^^
| ''plt.plot()'' | ''sbplt.plot()'' | 折れ線グラフの描画 |
| | ''sbplt.scatter()'' | 散布図 |
| | ''sbplt.hist()'' | ヒストグラム |
| | ''sbplt.imshow()'' | ヒートマップ |
| | ''sbplt.pie()'' | 円グラフ |
| | | 箱ひげ図 boxplot |
^ ^^^
| ''plt.title('hoge')'' | ''sbplt.set_title('hoge')'' | グラフタイトル |
| ''plt.xlabel()'' | ''sbplt.set_xlabel()'' | x軸の名前 |
| ''plt.ylabel()'' | ''sbplt.set_ylabel()'' | y軸の名前 |
| ''plt.xlim(left, right)'' | ''sbplt.set_xlim(left, right)'' | x軸の値の範囲 |
| ''plt.ylim(bottom, top)'' | ''sbplt.set_ylim(bottom, top)'' | y軸の値の範囲 |
| ''plt.xticks([])'' | ''sbplt.set_xticks([],minor=False)'' | x軸の目盛りを設定 |
| ''plt.yticks([])'' | ''sbplt.set_yticks([],minor=False)'' | y軸の目盛りを設定 |
| ''plt.xticks(fontsize=10)'' | ''sbplt.ticks_params(axis="x", labelsize=10)'' | x軸の目盛りのfontsizeを設定 |
| ''plt.legend( bbox_to_anchor=(1,1), loc='upper right')'' | ''sbplt.legend( bbox_to_anchor=(1,1), loc='upper right')'' | 凡例を付ける.plot時に ''label='' で設定. |
^ ^^^
| | ''sbplt.vlines()'' | |
| | ''sbplt.hlines()'' | |
^ ^^^
| ''plt.subplots_adjust()'' | ''fig.subplots_adjust()'' | 余白の調整 |
| | ''fig.savefig('hoge.pdf')'' | 図の保存 |
=== 凡例(legend)の配置 ===
| ''bbox_to_anchor'' | 凡例の外接矩形のどこを基準点とするか.左下を ''(0,0)'' 右上を ''(1,1)'' とする配置.''(x軸,y軸)'' |
| ''loc'' | 置く場所.同様に左下を ''(0,0)'' 右上を ''(1,1)'' とする配置.文字だと ''upper'', ''lower''など |
=== 余白の調整(subplots_adjust) ===
''subplots_adjust()'' のキーワード引数解説。\\
''left'', ''right'', ''bottom'', ''top'' はすべて座標.幅のことではない。\\
''wspace'', ''hspace'' は幅のこと
^ 変数 ^ 意味 ^ 初期値 |
| ''left'' | 左 | 0.125 |
| ''bottom'' | 下 | 0.1 |
| ''top'' | 上 | 0.9 |
| ''right'' | 右 | 0.9 |
| ''wspace'' | 図同士の間(幅) | 0.2 |
| ''hspace'' | 図同士の間(高さ) | 0.2 |
=== その他 ===
* 目盛りがオフセット表記(=''x+2013''みたいな表記)になってしまうのを防ぐ\\ ''sbplt.get_yaxis().get_major_formatter().set_useOffset(False)''
* 目盛りを3桁区切りにする\\ ''sbplt.yaxis.set_major_formatter(''\\ ''ticker.FuncFormatter(lambda x, loc: '{:,}'.format(int(x))))''
* ''matplotlib.ticker''
* 縦線を引く ''sbplt.vlines([880], 0, 262635, 'blue', linestyles='dashed')''
* 横線を引く''sbplt.hlines([262635], 0, 1000, 'blue', linestyles='dashed')''
* 線のスタイルについては [[https://python.atelierkobato.com/linestyle/]] 参考
* "solid", "dashed", "dashdot", "dotted"
* 線の太さは linewidth
* 片対数グラフ ''sbplt.set_yscale('log')''
===== 折れ線グラフ =====
* [[https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.plot.html]]
sbplt.plot(xdata, ydata)
===== ヒストグラム =====
* [[https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.hist.html|doc]]
=== 簡単コード ===
fig = plt.figure()
sbplt = fig.add_subplot()
sbplt.hist(hoge)
fig.savefig( Path('hoge.pdf') )
plt.close()
=== histのパラメータ ===
| bins | 棒の個数 |
=== 二次元ヒストグラム ===
ヒートマップのこと
===== ヒートマップ =====
seaborn は使わなくて良い\\
[[https://matplotlib.org/stable/gallery/images_contours_and_fields/image_annotated_heatmap.html]]
=== matplotlib only でのサンプル ===
''sbplt.imshow'' を使う。
import matplotlib.pyplot as plt
import numpy as np
data = np.array([[0,0,2,2],
[0,0,2,2],
[2,2,2,3],
[2,2,3,3]])
fig = plt.figure()
sbplt = fig.add_subplot()
sbplt.imshow( data ,cmap='jet')
sbplt.set_xticks([])
sbplt.set_yticks([])
H,W = data.shape
for i in range(H):
for j in range(W):
_ = sbplt.text(j, i, int(data[i,j]),
ha="center", va="center", color="w")
fig.savefig('/tmp/hoge.pdf')
=== グリッド線の描画 ===
メジャー目盛りとは別にマイナー目盛りを設定する。\\
マイナー目盛りに線を描画するだけ。
H,W = tensor.shape
sbplt.set_xticks( np.arange(-0.5, H, 1), minor=True)
sbplt.set_yticks( np.arange(-0.5, W, 1), minor=True)
sbplt.grid(which='minor', color='w', linestyle='-', linewidth=2)
=== seaborn でのサンプル ===
import matplotlib.pyplot as plt
import seaborn
hoge # ndarray (H,W)
fig = plt.figure()
sbplt = fig.add_subplot()
sbplt = seaborn.heatmap(hoge, vmin=0.0,vmax=1.0, annot=True, fmt='1.3f', ax=sbplt)
sbplt.figure.savefig('hoge.pdf')
===== 散布図・棒グラフ・円グラフ =====
* ''.scatter''
* ''.bar''
* ''.pie''
* ax.pie(data, colors=colors, startangle=90, counterclock=False)
===== カラーマップの自作 =====
* 既にあるものは [[https://matplotlib.org/stable/gallery/color/colormap_reference.html#sphx-glr-gallery-color-colormap-reference-py]]
* カラーマップの自作 = ''LinearSegmentedColormap.from_list()''
* 凡例カラーバーの作成 = ''plt.cm.ScalarMappable()''
from matplotlib.colors import LinearSegmentedColormap
processed # list of ndarray, len=256
processed[0] # ndarray, shape (3,) RGB range [0.0, 1.0]
my_cmap = LinearSegmentedColormap.from_list( 'my_name', processed )
my_norm = plt.Normalize(vmin=myvmin,vmax=myvmax)
# https://matplotlib.org/3.3.2/api/_as_gen/matplotlib.colors.Normalize.html
my_smap = plt.cm.ScalarMappable(cmap=my_cmap, norm=my_norm)
# 描画
## heatmapの描画
## return
_polycollection = sbplt.pcolor(grid_data, vmin=myvmin,vmax=myvmax, edgecolors='gray', cmap=my_cmap)
# https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.pcolor.html?highlight=pcolor#matplotlib.axes.Axes.pcolor
# 描画設定
## 正方形に
## return None
_ = sbplt.set_aspect("equal")
## x軸の数値を描画しない
## return list of
_ = sbplt.set_xticklabels([])
## y軸の数値を描画しない
## return list of
_ = sbplt.set_yticklabels([])
## カラーマップを可視化 # Axes増える
## return
_ = sbplt.figure.colorbar(my_smap)
# https://matplotlib.org/3.3.2/api/_as_gen/matplotlib.figure.Figure.html?highlight=colorbar#matplotlib.figure.Figure.colorbar
memo : ''SakuData/config/cvpr/figure/maskgen_fig.py''
===== 文字の描画 =====
==== 軸目盛の削除 ====
目盛りの文字を削除するのには
ax.get_xaxis().set_ticks([])
を使う。
必要に応じて ''.annotate()'' を使うのが良い。
==== 描画 ====
ax.annotate('your text',
xy=(0.5, 0.5), # 座標
xycoords='axes fraction', # 座標の考え方
ha='right', # 座標アンカー位置をテキストボックスのどこにするか、横
va='bottom', # 座標アンカー位置をテキストボックスのどこにするか、縦
fontsize=9, # テキストのフォントサイズ
rotation=40, # テキストボックスの回転角度(度)
rotation_mode='anchor' # テキストボックス回転の中心
annotation_clip=False # ax の箱外にいるときに描画されない設定を消す
)
* xycoords
* ''axes fraction'' : ax を 横=0~1, 縦=0~1 とみなしたときの座標
* ''data'' : ax のデータの座標系での位置
* ''figure fraction'' : fig を 横=0~1, 縦=0~1 とみなしたときの座標
*
===== グリッドベースレイアウト =====
* 参考: https://github.com/yuuho/ccldataset/blob/2f6bac605bb1745bbe42d6267dae9514dd924516/projects/old_torch_ccl/torch_gpu_ccl/video_writer.py#L414
* gridspec, grid_spec
spec = fig.add_gridspec()
ax = spec[:, :]
gridH = 12
gridW = 8
# 0 1 2 3 4 5 6 7 8
# 0 |^^^^^| |^^^^^^^^^^^^| |^^^^| | |^^^^|
# 1 | | | | | | | | |
# 2 | | | | |vvvv| | | |
# 3 |vvvvv| | | |^^^^| | |vvvv|
# 4 |^^^^^| | | | | | |^^^^|
# 5 | | | | |vvvv| | | |
# 6 | | | | |^^^^| | | |
# 7 |vvvvv| | | | | | |vvvv|
# 8 |^^^^^| | | |vvvv| |^^^^^^|
# 9 | | | | |^^^^| | |
# 10 | | | | | | | |
# 11 |vvvvv| |vvvvvvvvvvvv| |vvvv| |vvvvvv|
w_img = 1 # セグメンテーション画像の幅
w_lin = 0.78 # セグメンテーション画像とヒストグラムの隙間
w_his = 5 # ヒストグラムの幅
w_ma1 = 0.05 # ヒストグラムとサンプリング画像の隙間
w_fet = 0.7 # サンプリング画像の幅
w_ma2 = 0.1 # サンプリング画像と出力画像の隙間
w_ma3 = 0.2 # グラフ左と出力画像左の隙間
w_gra = 1 # グラフ幅
width_ratios = [w_img, w_lin, w_his, w_ma1, w_fet, w_ma2, w_ma3, w_gra]
===== 消す系 =====
=== 図の枠線 ===
ax.spines['left'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['bottom'].set_visible(False)
=== 図の目盛り ===
ax.set_xticks([])
ax.set_yticks([])
=== 図の背景 ===
ax.patch.set_facecolor('none')
fig.patch.set_facecolor('none')
===== 日本語フォント =====
=== Ubuntu でのフォントディレクトリ ===
* ''/usr/share/fonts''
**メイリオ(meiryo)を使いたい場合** は Windows から引っこ抜いてくるのが良さそう。
* ''C:%%\%%\Windows\Fonts'' にある ''.ttc'' ファイルは使えそう。
* WSL で言うところの ''/mnt/c/Windows/Fonts''
* メイリオは以下二つ
* ''/mnt/c/Windows/Fonts/meiryo.ttc''
* ''/mnt/c/Windows/Fonts/meiryob.ttc''
* mkdir /usr/share/fonts/meiryo
=== フォントの設定やフォントをアウトライン化して出力する設定 ===
from matplotlib import rcParams
rcParams['font.family'] = 'sans-serif'
rcParams['font.sans-serif'] = ['Noto Sans CJK JP']
rcParams['svg.fonttype'] = 'path'
=== フォントの確認 ===
import matplotlib.font_manager as fm
fonts = set([fm.FontProperties(fname=font).get_name() for font in fm.findSystemFonts()])