以下の内容はPython 3.5.2 + matplotlib 1.5.1およびPython 3.6.2 + matplotlib 2.0.2で検証した. 作業日は2017年10月22日, 前者はUbuntu 16.04 on Win10 (WSL), 後者はDebian 8.9 (Anaconda).
規格化したいのにできない
なにか数値の列
data
があったとして, そのヒストグラムをmatplotlibでプロットしたいとする. 普通に
plt.hist( data )
とすると, これは縦軸が各bin内に入るデータ点が何個あるかを表すことになる.
これをデータ総数len(data)
で規格化したプロットにしようと思って
plt.hist( data, normed=True )
またはnormed=1
とかやっちゃうと, 思った通りのアウトプットにならずに頭を傾げることになる. 例えば:
import numpy as np
import matplotlib.pyplot as plt
data = np.random.normal(0,0.1,1000)
weights = np.ones(len(data))/len(data)
plt.hist( data, weights=weights )
plt.show()
アウトプットは
で, 縦軸が1を超えるとか, 意味がわからない.
原因
matplotlibのドキュメントを見ても何も書いてない.
これはnumpyのドキュメントに答えが書いてあるからで,
要するに
normed
オプションは事実上density
オプションと等しく, これは縦軸を確率分布関数と思って規格化するオプションである, と.
従って, normed=True
オプションを指定すると, binの面積が1に規格化されることになる.
いま欲しいものは値の総和が1に規格化されたアウトプットなのだから, binの幅が1でない限り, 欲しい結果は得られない.
対策
代わりにweights
オプションを指定すればこの問題は解決できる.
これはdata
の1つの値の重みを指定するパラメータで, デフォルトでは実質的に
weights = np.ones(len(data))
を指定していることになる. いま総和を1にしたいのだから, ひとつのデータの重みは1/len(data)である. 故に
weights = np.ones(len(data))/float(len(data))
plt.hist( data, weights=weights )
とすれば欲しいグラフが手に入る.
コメント
コメントを投稿