web-dev-qa-db-ja.com

matplotlibを使用した離散値のヒストグラム

Matplotlibを使用して離散値のヒストグラムを作成する必要がある場合があります。その場合、ビニングの選択が重要になる可能性があります。10個のビンを使用してヒストグラム[0、1、2、3、4、5、6、7、8、9、10]にした場合、ビンの1つは2倍になります。他のものと同じくらい数えます。つまり、binsizeは通常、離散化サイズの倍数である必要があります。

この単純なケースは自分で比較的簡単に処理できますが、=のために離散化サイズがわずかに変化する可能性がある浮動小数点データの場合を含め、これを自動的に処理するライブラリ/関数へのポインタがありますか? FP丸め?

ありがとう。

18
antony

あなたの質問のタイトルを考えると、離散化サイズは一定であると仮定します。

この離散化サイズ(または、少なくとも、厳密には、n倍のサイズを見つけることができます。データに2つの隣接するサンプルがないこと)

np.diff(np.unique(data)).min()

これにより、データ(np.unique)内の一意の値が検出され、それらの間の差異(np.diff)が検出されます。ゼロ値を取得しないように、一意が必要です。次に、最小の差を見つけます。これには、離散化定数が非常に小さい場合に問題が発生する可能性があります。これについては後で説明します。

次に、値をビンの中央に置きます-現在の問題は、9と10の両方が、matplotlibが自動的に提供する最後のビンの端にあるため、1つのビンで2つのサンプルを取得するためです。

だから-これを試してください:

import matplotlib.pyplot as plt
import numpy as np

data = range(11)
data = np.array(data)

d = np.diff(np.unique(data)).min()
left_of_first_bin = data.min() - float(d)/2
right_of_last_bin = data.max() + float(d)/2
plt.hist(data, np.arange(left_of_first_bin, right_of_last_bin + d, d))
plt.show()

これは与える:

Histogram of sample data


小さな非整数の離散化

テストデータセットをもう少し作成できます。

import random 

data = []
for _ in range(1000):
    data.append(random.randint(1,100))
data = np.array(data)
nasty_d = 1.0 / 597 #Arbitrary smallish discretization
data = data * nasty_d

次に上記の配列を実行して、コードが吐き出すdを確認すると、

>>> print(nasty_d)
0.0016750418760469012
>>> print(d)
0.00167504187605

したがって、検出されたdの値は、データの作成に使用されたnasty_dの「実際の」値ではありません。ただし、ビンをdの半分だけシフトして、中央の値を取得するトリックを使用すると、問題はありませんunless離散化が非常に小さいため、フロートの精度の限界に達しましたまたは数千のビンがあり、検出されたdと「実際の」離散化との差は、ビンの1つがデータポイントを「見逃す」ようなポイントまで増加する可能性があります。これは知っておくべきことですが、おそらくあなたを襲うことはありません。

上記のプロット例は次のとおりです

Example histogram with small discretization


不均一な離散化/最も適切なビン...

さらに複雑なケースについては、 このブログ投稿を見つけました をご覧ください。これは、独自のベイジアンを開発する前に Sturges 'ルールとFreedmanおよびDiaconisルール などの複数の標準的な手法を参照して、(連続/準連続)データから最適なビン幅を自動的に「学習」する方法を検討します。動的プログラミング方法。

これがあなたのユースケースである場合-質問がはるかに広く、Stack Overflowでの決定的な回答には適さない可能性がありますが、リンクが役立つと思います。

22
J Richard Snape

おそらく J Richard Snape's よりも完全な答えではありませんが、私が最近学んだことと、直感的で簡単だと思った答えです。

import numpy as np
import matplotlib.pyplot as plt

# great seed
np.random.seed(1337)

# how many times will a fair die land on the same number out of 100 trials.
data = np.random.binomial(n=100, p=1/6, size=1000)

# the trick is to set up the bins centered on the integers, i.e.
# -0.5, 0.5, 1,5, 2.5, ... up to max(data) + 1.5. Then you substract -0.5 to
# eliminate the extra bin at the end.
bins = np.arange(0, data.max() + 1.5) - 0.5

# then you plot away
fig, ax = plt.subplots()
_ = ax.hist(data, bins)
ax.set_xticks(bins + 0.5)

enter image description here

約16/100スローが同じ数になることがわかります!

11
Manuel Martinez

少量のコードで単純なケースを処理するための別のバージョン!今回は numpy.unique および matplotlib.vlines

import numpy as np
import matplotlib.pyplot as plt

# same seed/data as Manuel Martinez to make plot easy to compare
np.random.seed(1337)
data = np.random.binomial(100, 1/6, 1000)

values, counts = np.unique(data, return_counts=True)

plt.vlines(values, 0, counts, color='C0', lw=4)

# optionally set y-axis up nicely
plt.ylim(0, max(counts) * 1.06)

私に与える:

matplotlib output

非常に読みやすく見える

2
Sam Mason