====== 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()])