読者です 読者をやめる 読者になる 読者になる

HPCメモ

HPC(High Performance Computing)に関連したりしなかったりすることのメモ書き

pandas+matplotlibで3D棒グラフを作る

たとえば、こんな感じの表があったとするじゃないですか。

うし ぶた とり
肉屋 580 280 300
スーパー 420 260 320

これを、エクセルに入力して、挿入->3D-縦棒と選ぶと

f:id:n_so5:20151008183105p:plain

こんな感じのかっこいい(?)グラフが作れるわけですが、これをpandas+matplotlibでやってみようというお話です。

結論から先に書くと

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.font_manager import FontProperties

df=pd.DataFrame({u"うし":[580, 420],
                 u"とり":[280, 260],
                 u"ぶた":[300, 320]},
               index=(u"肉屋", u"スーパー"))

ax=plt.figure().add_subplot(111, projection='3d')
fp=FontProperties(fname=r'C:\WINDOWS\Fonts\msgothic.ttc', size=10)

xpos,ypos=np.meshgrid(np.arange(len(df.index)), np.arange(len(df.columns)))
xpos=xpos.flatten()
ypos=ypos.flatten()
zpos=np.zeros(len(xpos))
dx=np.full(len(xpos), 0.05)
dy=np.full(len(ypos), 0.1)
dz=df.as_matrix().flatten()
ax.bar3d(xpos, ypos, zpos, dx, dy, dz)

ax.set_xlabel(u"お店", fontproperties=fp)
ax.set_ylabel(u"種類", fontproperties=fp)
ax.set_zlabel(u"金額", fontproperties=fp)
plt.xticks(np.arange(len(df.index)),   df.index, fontproperties=fp)
plt.yticks(np.arange(len(df.columns)), df.columns, fontproperties=fp)

plt.axis([-0.5, len(df.index)-0.5, -0.5, len(df.columns)-0.5])

plt.savefig("hoge.png")

みたいなコードになります。


公式ページのexampleを見ると、
mplot3d example code: bars3d_demo.py — Matplotlib 1.4.3 documentation
ってのがありますが、こっちは普通の棒グラフを奥行方向に重ねて並べるもののようで、今回作ろうと思っていたのとはちょっと違います。

一見関係無さそうな名前ですが、
mplot3d example code: hist3d_demo.py — Matplotlib 1.4.3 documentation
のところで使っている、bar3d()が今回のグラフに必要な関数でした。
ところがこの関数、APIドキュメントがこんな調子でまったく意味不明です。

bar3d(x, y, z, dx, dy, dz, color=u'b', zsort=u'average', *args, **kwargs)

Generate a 3D bar, or multiple bars.

When generating multiple bars, x, y, z have to be arrays. dx, dy, dz can be arrays or scalars.

color can be:

A single color value, to color all bars the same color.
An array of colors of length N bars, to color each bar independently.
An array of colors of length 6, to color the faces of the bars similarly.
An array of colors of length 6 * N bars, to color each face independently.

When coloring the faces of the boxes specifically, this is the order of the coloring:

-Z (bottom of box)
+Z (top of box)
-Y
+Y
-X
+X

Keyword arguments are passed onto Poly3DCollection()

http://matplotlib.org/mpl_toolkits/mplot3d/api.html#mpl_toolkits.mplot3d.axes3d.Axes3D.bar3d

x, y, z have to be arraysとか言われても、何の値入れりゃえんじゃーと悩んでましたが、どうやら

  • 各配列は全て同じ長さの1次元配列にする
  • i番目の棒の左下隅の頂点座標をx, y, zのi番目の要素で指定
  • i番目の棒の各軸方向の幅をdx, dy, dzのi番目の要素で指定

ということのようです。

ちなみに、この例ではx、yともに項目名なのであまり違和感は無いかもしれませんが、元々私がやってた時は2次元でパラメータを振った時の実験結果をプロットしようとしていたので、x, y, zに値を入れてからdx, dy, dzって何を入れるんだろうかと、かなりドツボにはまってました。*1

あとのところは、コードを見れば分かると思いますので、解説は省きますが

set_?label
ラベルの設定
{x,y}ticks
x/y座標の値の指定
axis
座標軸の長さ指定
savefig
グラフを画像ファイルとして保存

です。


matplotlibの全般的な話は
matplotlib入門 - りんごがでている
に助けられました。ここ読まなかったら未だドキュメントの海で溺れてたと思いますorz

あと、フォント関連の設定については
Python - matplotlibで日本語 - Qiita
を参考にさせていただきました。



そういや、pandasろくに使って無いな・・・

*1:素直にwire frameでやってればそんなにはまらなかった気がしないでもない