Pythonでデータ処理をしていると表(.table)を扱いたくなる時があります。しかしデフォルトの表があまりにもプアな見た目なので、ここでは.tableを色々とカスタマイズしていく方法を紹介します。
こんにちは。wat(@watlablog)です。ここではmatplotlibで表を描く時に使う.tableをカスタマイズしてみます!
.tableを使って表を描くコード
デフォルト
まずは百聞は一見に如かずという事で、何も考えないで.tableでデフォルトの表を描いてみます。
サンプルのデータはリアリティを出すために、実際に振動の工学計算から求めた数値を使っています。dataが2Dデータで、axis1とaxis2が各軸の数値です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import numpy as np from matplotlib import pyplot as plt data = np.array([[1.00000000e+00, 4.93860375e-01, 3.80529004e-01, 2.07607315e-01, 2.08212102e-03, 1.02646060e-01, 2.57213985e-02], [4.93860375e-01, 1.00000000e+00, 2.02592790e-01, 6.66213564e-01, 4.97280547e-02, 2.39123942e-02, 9.45866643e-02], [3.80529004e-01, 2.02592790e-01, 1.00000000e+00, 1.05322338e-01, 1.59472479e-01, 5.24966108e-01, 1.78456988e-02], [2.07607315e-01, 6.66213564e-01, 1.05322338e-01, 1.00000000e+00, 3.33062289e-01, 2.60005204e-04, 1.75998325e-01], [2.08212102e-03, 4.97280547e-02, 1.59472479e-01, 3.33062289e-01, 1.00000000e+00, 5.37262027e-01, 4.92917383e-02], [1.02646060e-01, 2.39123942e-02, 5.24966108e-01, 2.60005204e-04, 5.37262027e-01, 1.00000000e+00, 5.13000443e-03], [2.57213985e-02, 9.45866643e-02, 1.78456988e-02, 1.75998325e-01, 4.92917383e-02, 5.13000443e-03, 1.00000000e+00]]) axis1 = np.array([0.66500943, 18.73672957, 20.90599893, 28.01849681, 33.55060941, 36.68665363, 55.8380484]) axis2 = np.array([0.66500943, 18.73672957, 20.90599893, 28.01849681, 33.55060941, 36.68665363, 55.8380484]) # ここから表作成---------------------------------------- # fig準備 fig = plt.figure() ax1 = fig.add_subplot(111) # 表の定義 ax1.table(cellText=data, colLabels=axis1, rowLabels=axis2, loc="center") # 表示 plt.show() plt.close() # --------------------------------------------------- |
結果は以下の図です。びっくりするくらいわかりにくいですね。
.tableを使うにはいくつかコツがいるようです。
グラフ軸消去/数値丸め/フォント変更
まず簡単に考えられる見栄え調整として、余計な軸を.axis('off')で消去し、数値をnp.round()で丸め、ついでに.rcParamsでフォントを変更したり.tight_layout()で配置調整します。
コードは表作成部分を以下に変更するだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# ここから表作成---------------------------------------- # フォントの種類とサイズを設定する。 plt.rcParams['font.family'] = 'Times New Roman' # fig準備 fig = plt.figure() ax1 = fig.add_subplot(111) # 表の定義 ax1.axis('off') ax1.table(cellText=np.round(data,2), colLabels=np.round(axis1,2), rowLabels=np.round(axis2,2), loc="center") # レイアウト設定 fig.tight_layout() # 表示 plt.show() plt.close() # --------------------------------------------------- |
以下が結果です。先ほどよりは表らしさが出てきました。しかしちょっとイマイチなので、これをベースにカスタマイズしていきます。
.tableをカスタマイズする方法
cellColoursでセルに色をつける
閾値で色分けをする
以下は「数値が0.7以上のセルを赤くする」という処理を行った表です。
この表の作成のために、dataと同じサイズのcolor配列を作成し、if文により色を振り分けています。
1 2 3 4 5 6 7 8 |
# data配列で0.7以上の数値を赤くするためのカラー配列を作成 color = np.full_like(data, "", dtype=object) for i in range(len(data)): for j in range(len(data.T)): if data[i, j] >= 0.7: color[i, j] = 'red' else: color[i, j] = 'white' |
np.fullでdtype=objectとしている理由は、strにすると文字数制限がかかってしまうためです。以下のQiita記事で解決したので、参考に引用しておきます。
np.emptyで確保されたのは1文字分だけなので、AXやBYやCZを格納することができないわけだ。
2文字以上の文字列でもってnp.fullしたときもdtype=strを指定すると1文字になってしまう。さすがにこれは意味不明だ。任意の文字数の文字列を格納できるようにするには、dtype=str でなくdtype=objectと指定する。
Qiita:numpyで文字列の配列を作る際の注意点
表定義部分にcellColoursを指定してあげる事で先ほどの表を描く事ができます。
1 2 3 4 5 |
ax1.table(cellText=np.round(data,2), colLabels=np.round(axis1,2), rowLabels=np.round(axis2,2), loc="center", cellColours=color) |
ちなみに、colorはアメリカ英語、colourはイギリス英語らしいです。
cmapに対応させて色付けする
cmap(カラーマップ)と呼ばれる数値の大小と色が紐付いたマップを使う事で、連続的な色の変化を可視化する事が可能です。matplotlibでは様々な色コードが使えます。以下の公式ドキュメントをご参考に。
公式ドキュメント:matplotlib color example code
ここで作るのは以下の表です。
以下が上表を実現させるためのcolor配列の作り方です。
今回のデータは既に最小値が0、最大値が1になっていましたが、そうでないデータはMin-Max正規化を使って値を強制的に0から1の範囲に納めます。
そして、plt.get_cmapを使ってカラーマップを取得、サイズを合わせたcolorにそれぞれ対応させます。
1 2 3 4 5 6 7 |
# dataをMin-Max正規化 norm_data = (data - np.min(data)) / (np.max(data) - np.min(data)) # cmapを使ってデータのカラー配列を作る cm = plt.get_cmap('coolwarm') color = np.full_like(data, 0, dtype=object) color = cm(norm_data) |
cmapを覚えると表を画像としても見せる事ができ、説得力が増します!
cmapを設定するとカラーバーを追加したくなります。しかしplt.colorbar()を直接tableに設定すると「AttributeError: 'Table' object has no attribute 'get_array'」とエラーとなってしまいます。
そこで今回はちょっと荒技ですが、ダミーで画像プロットをimshowで作成し、その後imshowのデータと軸を消去する方法をとってみました(以下のコード)。
ax_dummyとax1の順番も重要です。これが逆だと白い画面にカラーバーのみという表示になってしまいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# fig準備 fig = plt.figure() ax1 = fig.add_subplot(111) # colorbarのためのDummyを作成 ax_dummy = fig.add_subplot(111) im = ax_dummy.imshow(norm_data, cmap='coolwarm') ax_dummy.cla() ax_dummy.axis('off') # colorbarを設定 cbar = plt.colorbar(im) cbar.set_label('MAC') # 表の定義 ax1.axis('off') the_table = ax1.table(cellText=np.round(data,2), colLabels=axis1, rowLabels=axis1, loc="center", cellColours=color) |
結果はこちら。ちょっとcolorbarのサイズが大きいのが気になります。
「MatplotlibDeprecationWarning:」が出ますが、これはあえてやっているので気にしません。
これは下で紹介している「表のサイズをfigure全体にフィットさせる」を使うと改善されます。
ただやはりcolorbarのレイアウト設定は結構難解です…。組み合わせによってレイアウト崩れを起こしたりと、まだまだ使いこなすには知識が必要です。
軸に共通の文字列を追加する
こちらは軸に共通の文字列として「[Hz]」を追加した表です。工学的な表は数値データとなる事が多いですが、単位を書いた方がわかりやすい時もあります。
上の表を作るために、まずはaxis1, axis2のデータを以下のように加工します。
数値として行う丸め処理は先にやっておき、文字列に変換、その後にobjectに変換して演算子が使える状態にした後で共通の文字を「+ '[Hz]」と追加してみました。
1 2 3 |
# 各軸を文字列に変換して共通の文字列をさらに追加する axis1 = np.round(axis1, 2).astype(str).astype(object) + '[Hz]' axis2 = np.round(axis2, 2).astype(str).astype(object) + '[Hz]' |
表を定義する部分は以下のようにシンプルにさせます。
1 2 3 4 |
ax1.table(cellText=np.round(data,2), colLabels=axis1, rowLabels=axis2, loc="center") |
表のサイズをfigure全体にフィットさせる
先ほどまでの表は、下図のように全体に対して大分余白の大きい結果でした。図として貼り付けると無駄が多いので…
以下のようにfigure sizeいっぱいに表をフィットさせるようにしてみます。
以下コードを表定義の下に記述します。
1 2 3 |
# 表をfigure全体に表示させる for pos, cell in the_table.get_celld().items(): cell.set_height(1/len(data)) |
ここは考えてもわかりませんでしたが、ググった結果以下の外部サイト様を見つけました。検索ってすごい。
表と図のサイズを「同期」して、表が完全に図の中に収まるようにする方法がわかりません。
>>セルの幅は、デフォルトで、軸の幅に合うように自動的に調整されます
Javaer101 : matplotlibのテーブルと図のサイズの関係loc="center"
。
残っているのは、セルの高さを設定することです。
これは、軸座標の単位で示されます。
したがって、軸の高さ全体(軸座標で== 1)を埋めるために、1をテーブルの行数で割って、各セルの高さを取得できます。
次に、高さをすべてのセルに設定します。
この方法であれば、figsize=()を指定するだけでいつでも図のサイズにフィットした表を作成する事ができます。
以下は「fig = plt.figure(figsize=(8,2))」の結果。
まとめ
本記事ではmatplotlibの.tableを使って表を描く色々な方法を紹介しました。
デフォルトのアウトプットで上司報告等を行うと怒られてしまうかも知れませんので、是非最低限の見栄えは気にしましょう。
大半の人は.csvやExcelファイルに結果を出力して、表計算ソフトでグラフ表示や表の見栄えを整えるという事をしている事でしょう。
しかしPythonのようなハードルの低いプログラミング言語でグラフや表を使いこなす事で、逐一人の手が入らなくなるというメリットがあります。
何度も結果を確認しながら試行錯誤する業務等にはPythonはもってこいだと思いますので、是非やってみて下さい!
.tableの使い方がなんとなくわかってきました!表関係はこちらの記事に追記していきます!
Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!
初めまして!!
最近パイソンを勉強し始めたので、読ませていただいてます!!
とても分かりやすくためになります、これからも頑張ってください
ご訪問ありがとうございます。
励ましのお言葉嬉しいです!
これからも記事を追加していきますのでよろしくお願い致します!
.table関係のまとめが少なく、端的にまとめてあるこのホームページは素晴らしいです。
段階を踏んで例に分けてあるのが、わかりやすいです。
gridspec.GridSpec
add_subplot
を使って絵を1枚作ったのですが、
グラフを大きく作って、サマリーの表を右上に貼りつける必要がありました。
そのさいの表のサイズを調整するのに、この記事を活用させていただきました。
ありがとうございました。