#matplotlibでhistgram
matplotlibでヒストグラムを作る際にハマったことのメモ
#normedの落とし穴
matplotlibでヒストグラムをつくるときに使う matplotlib.axes.Axes.hist() もしくは matplotlib.pyplot.hist() には恐ろしい落とし穴が存在するのでメモしておく。ヒストグラムを作るときに頻度を正規化するためにnormed=1というオプションをつけることが多々ある。しかし、このoptionをつけたのにも関わらず、以下のようなヒストグラムになることがある。
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(0)
nor = np.random.normal(0,0.5,1000)
plt.hist(nor,normed=1,range=(-3,3),bins=300)
plt.savefig("test.pdf")
え、y軸の目盛りの値が1より大きい。。
当初、これをバグだと思っていた筆者は無理やりbarplotを用いてヒストグラムを書こうとした。しかし、面倒過ぎる。seabornのdistplotならと思ってみたが、同じ問題を抱えていてダメ。謎だなぁと思っていたら、あることに気づいた。
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(0)
nor = np.random.normal(0,0.5,1000)
plt.hist(nor,normed=1,range=(-3,3),bins=6)
plt.savefig("test.pdf")
binの幅が1なら正常に動くじゃん。 まさか、、
なんと、binの面積の合計が1になるように正規化されている。 実際に、numpyのhistogram関数(matplotlibのhistと挙動は一緒)をつかってデータの詳細をみてみると
import numpy as np
np.random.seed(0)
nor = np.random.normal(0,0.5,1000)
hist,pos = np.histogram(nor,normed=1,range=(-3,3),bins=300)
print(np.sum(hist))
#50.0
print(np.sum(hist)*6.0/300)
#1.0
となる。よって、以下のようにコードを書きかえて上げればいい。
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(0)
nor = np.random.normal(0,0.5,1000)
binnum = 300
fig = plt.figure()
ax = plt.subplot(111)
ax.hist(nor,normed=1,range=(-3,3),bins=binnum)
ax_yticklocs = ax.yaxis.get_ticklocs()#目盛りの情報を取得
ax_yticklocs = list(map(lambda x: x * len(range(-3,3))*1.0/binnum, ax_yticklocs))#元の目盛りの値にbinの幅を掛ける
ax.yaxis.set_ticklabels(list(map(lambda x: "%0.2f" % x, ax_yticklocs)))直した目盛りを表示
plt.savefig("test.pdf")
これで、多分望みどおりの目盛りができてるはず。しかし、この実装はわかりにくい、、、。