質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.77%

確率密度とは何を表しているのでしょうか?

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,615

aae_11

score 134

「Jupiter notebook」にて、分布図の確認を行っていたのですが、確率密度?の見方が分からなかったため、質問させてもらいました。
以下はデータの一覧(5つのレコードまで)です。
イメージ説明

そして、分布図が以下となります。
イメージ説明

こちらのヒストグラムの見方ですが、横軸がデータ(値)であるとの認識でおります。
その場合、「0.3 ~ 0.4」の範囲で、縦軸の数値「1」が対応しているかと思うのですが、この「1」は何を示しているのでしょうか...?
縦軸は確率密度らしいのですが、いまいち確率密度というのが分からなかった為、どなたかアドバイス頂けますと幸いです。
よろしくお願いします。

追記です

ご回答による頂いたコードをjupyter notebookに記載し、実行してみました。
イメージ説明

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+5

グラフの見方について

青のバーと青の折れ線は分けて考えてください。

seaborn.distplot の kde=True (デフォルト) の場合に、ヒストグラムの他にこのグラフが描画されます。
実装上は statsmodels がインストールされている場合は statsmodels.nonparametric.kde.KDEUnivariate、そうでない場合は scipy.stats.gaussian_kde に処理を投げるようになっています。

確率密度関数について

連続型の確率変数 X があるとき、X がある値 a 以下をとる確率を P(X <= a) としたとき、

イメージ説明

で定義される f(x) が確率密度関数です。

※ 確率変数 = とり得る値が確率によって決まる変数。例えば、X をサイコロの目としたとき、X = 1 (1の目) は、1/ 6 の確率で出る。

注意したいのは、f(c) の値は、X = c となる確率ではありません。
X = c となる確率 P(X = c) は定義に従って計算すると、0 になります。

確率密度関数の見方としては、確率密度関数 f(x) の値が大きいところは、そのあたりの x の値が現れる可能性が高いという理解でいいと思います。

体系的に理解したい場合は 統計学 の教科書を当たると、最初のほうで必ず紹介されていると思うので、それを参照してください。

追記

ストーリーとしては、以下のようになります。

いくつかのデータが得られた

データが得られた背景となる確率分布があるけど、それはわからない

データから確率分布を推定する


統計学の考え方としては、まずなんらかの真の確率分布があり (現実ではこれはわからない)、データはその確率分布から得られた標本 (値の例) であると考えます。

以下、例として、標準正規分布としておきます。

from scipy.stats import norm, gaussian_kde
import numpy as np
import matplotlib.pyplot as plt

# 平均0、分散1の1変量正規分布に従う確率変数
rv = norm(loc=0, scale=1)

fig, ax = plt.subplots()
ax.plot(xs, rv.pdf(xs), label="確率密度関数")
ax.legend(loc="upper left")
ax.set_xlim(-5, 5)
plt.show()

イメージ説明

このような分布があって、そこから1000 個の値が得られたとします。
ヒストグラムを描画すると以下のようになります。

イメージ説明

現実では、いくつかのデータが得られたとして、真の確率分布というのはわからないわけです。
例えば、100人に身長を聞いて、[168, 172, 180, ...] と100人分のデータが得られたとして、その元となった真の確率分布というのはわかりません。

わからないので、それを今持っているデータから推定するというのが、統計学の1つの目的になります。
カーネル密度推定はその手法の1つです。
カーネル密度推定を使って推定すると、以下のようになります。
元の分布 (青) がわからなくても、データからある程度それに近い分布 (オレンジ) が得られました。

# この得られた標本から元となる確率密度関数を推定する。
kernel = gaussian_kde(samples)

# 確率密度関数を描画する。
xs = np.linspace(-5, 5, 1000)

fig, ax = plt.subplots()
ax.plot(xs, rv.pdf(xs), label="確率密度関数")
ax.plot(xs, kernel(xs), label="推定した確率密度関数")
ax.legend(loc="upper left")
ax.set_xlim(-5, 5)
plt.show()

イメージ説明

追記

2つのグラフで「y」の値が、一方は少数、一方は整数となっておりますが、これは何故かがわかりません。

グラフの目盛りがすべて整数なら整数、小数が含まれるなら小数で表示するという matplotlib の仕様によるものなので、整数か小数かはグラフの表示上の問題であり、重要ではありません。
注目するべきは、y 軸のスケールが左と右で大きく違うことでしょう。
これは、他の回答者様のコメント欄で hayataka2049 さんがご指摘されていますが、ヒストグラムの正規化 (デフォルトで有効) によるものです。
通常、ヒストグラムは y 軸は各ビンに属するデータ数 (頻度値) ですが、この正規化が有効の場合、棒の面積の合計が1となるように棒の高さが調整されます。

数式で表すと、n 個のビンの幅が w1, w2, ..., wn、頻度値が f1, f2, ..., fn としたとき、
w1 * f1 + w2 * f2 + ... + wn + fn = 1
となるように f1, f2, ..., fn の値を正規化します。

左のほうが右より棒の高さが高いのは、左の方はビンの幅が小さいので、棒の面積の合計が1にするためには棒の高さをその分高くする必要があるからです。
棒の高さをそのビンに属するデータ数として解釈したい場合は distplot() で norm_hist=False を指定してください。

この正規化の仕様は、内部でヒストグラム作成に使用している numpy.histogram から来ています。

また、can110さんのご回答で、「描かれた曲線とx軸とを囲む領域の面積がデータ値の出現個数の総数になるように補正、正規化された値だということのようです。」とあるのですが、
仮に左側の画像の総面積を求めてみますと、x軸が「0.0 ~ 0.3」の為、「0.3」y軸を大体ではありますが「6」と考えた場合、「0.3 × 6 ÷ 2」 = 「0.9」となります。
こちらのデータ値の出現個数の総数が「0.9」であるという意味がいまいち分からない為、ご助言頂けましたら幸いです...

曲線のほうは、先の回答の通り、推定した確率密度関数です。
「描かれた曲線とx軸とを囲む領域の面積」とは、確率密度関数を (-∞, +∞) の区間で積分すると求められ、これは1になります。

イメージ説明

この計算の解釈は、確率変数 X は実数の値をとるので、(-∞, +∞) の間の値をとる確率は100%であるということを言っています。

補足

正規化されたヒストグラム、カーネル密度推定で推定した確率密度関数ですが、どちらも(不明な)真の確率密度関数を近似するのが目的です。

曲線とx軸の間の面積を求めるのに、x 軸を細かい区間で分割して、長方形を敷き詰めて、面積を計算するというのが、リーマン積分の考え方です。
なので、大量のデータを用意して、ビンの幅を小さくした正規化されたヒストグラムを作成すれば、それで真の確率密度関数を近似できます。

イメージ説明
リーマン積分の考え方

from scipy.stats import norm, gaussian_kde
import numpy as np
import matplotlib.pyplot as plt

# 平均0、分散1の1変量正規分布に従う確率変数
rv = norm(loc=0, scale=1)

# 大量の標本を生成する。
samples = rv.rvs(300000)

# この得られた標本から元となる確率密度関数を推定する。
kernel = gaussian_kde(samples)

fig, ax = plt.subplots()

# 確率密度関数を描画する。
xs = np.linspace(-5, 5, 1000)
ax.plot(xs, rv.pdf(xs), label="確率密度関数")
ax.plot(xs, kernel(xs), label="KDE で推定した確率密度関数")

# ヒストグラムを描画する。
bins = np.linspace(-5, 5, 1000)
ax.hist(samples, bins, density=True)
ax.legend(loc="upper left")
plt.show()

「カーネル密度推定の確率密度関数 (オレンジ)」は「真の確率密度関数」と一致して重なっている。
「正規化されたヒストグラム(緑)」も「真の確率密度関数」とほぼ一致している。

イメージ説明

追記 distplot の仕様確認

引数の優先度としては、norm_hist < kde < hist_kws のようですので、
正規化したくない場合は、kde=False または hist_kws={"density": False} を指定するのがよさそうです。

 kde   density   ヒストグラム 
 False   False   正規化されない 
 False   True   正規化される 
 True   False   正規化されない 
 True   True   正規化される 
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns

sns.set()
np.random.seed(0)

x = np.random.randn(1000)

fig, axes = plt.subplots(2, 2, figsize=(8, 8))
axes = axes.ravel()

axes[0].set_title("kde=False, density=False")
sns.distplot(x, kde=False, hist_kws={"density": False}, ax=axes[0])
axes[1].set_title("kde=False, density=True")
sns.distplot(x, kde=False, hist_kws={"density": True}, ax=axes[1])
axes[2].set_title("kde=True, density=False")
sns.distplot(x, kde=True, hist_kws={"density": False}, ax=axes[2])
axes[3].set_title("kde=True, density=True")
sns.distplot(x, kde=True, hist_kws={"density": True}, ax=axes[3])
plt.show()

イメージ説明

 kde   norm_hist   ヒストグラム 
 False   False   正規化されない 
 False   True   正規化される 
 True   False   正規化される 
 True   True   正規化される 
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns

sns.set()
np.random.seed(0)

x = np.random.randn(1000)

fig, axes = plt.subplots(2, 2, figsize=(8, 8))
axes = axes.ravel()

axes[0].set_title("kde=False, norm_hist=False")
sns.distplot(x, kde=False, norm_hist=False, ax=axes[0])
axes[1].set_title("kde=False, norm_hist=True")
sns.distplot(x, kde=False, norm_hist=True, ax=axes[1])
axes[2].set_title("kde=True, norm_hist=False")
sns.distplot(x, kde=True, norm_hist=False, ax=axes[2])
axes[3].set_title("kde=True, norm_hist=True")
sns.distplot(x, kde=True, norm_hist=True, ax=axes[3])
plt.show()

イメージ説明

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/02/11 11:38

    >この計算の解釈は、確率変数 X は実数の値をとるので、(-∞, +∞) の間の値をとる確率は100%であるということを言っています。
    なんとなく分かった気がいたします。
    言ってしまえば、全てのデータが含まれていることの証明だということですね

    キャンセル

  • 2020/02/11 16:25 編集

    > hayataka2049 さん

    コメントありがとうございます。
    仕様についてよく確認していませんでした。
    norm_hist=False でも kde=True の場合は正規化されてしまうので、正規化したくない場合は hist_kws={"density":False} を指定するのが、どのような場合でも効くのでよさそうですね。

    > aae_11 さん

    > しかしながら、実際の所、.単純にy軸のメモリをデータ数とした方が分かりやすい気がしたのですがこれは何故このような一見分かりにくい仕様となっているのか

    distplot() は、 名前にあるように、probability distribution (確率分布) をプロットするのが目的の関数のため、デフォルトでは KDE で推定した確率密度関数と正規化されたヒストグラムが描画されるようになっています。
    y 軸はデータ数としたヒストグラムを描画するのが目的の場合は、seaborn は使わないで、matplotlib の hist 関数を使ったほうが、混乱は少ないと思います。
    こちらはデフォルトで、y 軸はデータ数となっています。

    https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.hist.html

    キャンセル

  • 2020/02/11 17:31

    ご返信ありがとうございます。
    正直申しまして、ご回答頂いた大半の部分は分からない状況ではあるのですが、自分が機械学習の勉強を続け、また、高度な内容が理解できるようになった際は、再度ご回答を見返させて頂きたいと思います。
    この度は、ご親切にご解説くださりありがとうございました

    キャンセル

+2

What is y axis in seaborn distplot?にてほぼそのままの質問と回答がありましたので、まずは一読ください。

正直「確率密度」とは何かなどがいまいちほぼほぼ理解できていないのですが、Prasann Barotさんの回答をgoogle翻訳した結果を引用します。

ANS->密度プロットのy軸は、カーネル密度推定の確率密度関数です。ただし、これは確率ではなく確率密度であると指定するように注意する必要があります。違いは、確率密度とは、x軸上の単位あたりの確率です。実際の確率に変換するには、x軸上の特定の間隔の曲線の下の領域を見つける必要があります。やや紛らわしいことに、これは確率ではなく確率密度であるため、y軸は1より大きい値を取ることができます。

つまりy軸は確率密度を示す値ですが、描かれた曲線とx軸とを囲む領域の面積がデータ値の出現個数の総数になるように補正、正規化された値だということのようです。
これは以下のようなコードにて簡単な図を描くことで確かめられます。

以下の図において右側はx値が左側の10倍であるためy軸の値は1/10になっています。
またざっと目視で曲線とx軸で囲まれた領域の面積を求めてみます。
どちらも約1.25となり、実際の出現個数の総数4と、少し離れた値になっていますが、これは推定の仕方、distplotでデフォルトで使用している確率密度関数の性質に起因するものです。

import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

print(sns.__version__) # 0.9.0

fig, (ax1, ax2) = plt.subplots(1,2)
sns.distplot([0.1,0.1,0.1,0.2], ax=ax1)
sns.distplot([1,1,1,2], ax=ax2)
plt.show()


イメージ説明

曲線の意味

この図の曲線(確率密度関数)を用いると、与えられたデータ群以外のデータ値の出現確率(個数)を推定することができます。
たとえば左側の図においてx=0.075というデータ値の出現確率(個数)は以下によって求めることができます。

x値から上に直線を伸ばして曲線とそれに交わった点のy軸の値を見ます。この場合は約10になります。
これにx=0.075を掛けて0.75個という値がx=0.075の出現確率(個数)となります。
ただ、直感的にはもう少し大きな値になるのが自然そうで、あまり当てにならない感じもします。

ということでPrasann Barotさんの回答の続き

密度プロットの唯一の要件は、曲線下の総面積が1つに統合されることです。私は一般に、密度プロットのy軸を、異なるカテゴリ間の相対的な比較の値としてのみ考える傾向があります。

という回答になっているのかと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/02/09 15:38 編集

    ozwkさん、コメント&ご指摘ありがとうございます。
    ご指摘のとおり、ある一点0.075としてではなく、0.075~0.076 (Δx=0.001)という範囲の面積という計算が正しいですね。
    ただ、そもそもの確率分布関数(曲線)がsnsのバージョン違いのためかがaae_11と異なっており
    kde=True時のy軸の値の根拠、具体的にどのような計算で求めているのか推測でも分からず混乱しておりますが…
    こちらの図、および個数という解釈が誤りであるように思えてきました。
    (ちゃんと理論およびseabornの実装を理解すれば解決するはずですが、アタマが追いつきません)

    キャンセル

  • 2020/02/10 10:55 編集

    愉快なことに、distplotのbins引数を変えるとy軸のスケールは比例するかのように増加します。y軸の値を読むと、ヒストグラムの方はbinsに比例して値が大きくなります(グラフ上は同じ位置にあるように見える)。PDFの方はbinsに比例して値が大きくなったりはしません(グラフ上は潰れていくように見えます)。
    import seaborn as sns
    import matplotlib.pyplot as plt

    bins_lst = [5, 10, 20, 40, 80]
    fig, axes = plt.subplots(ncols=5)

    for b, ax in zip(bins_lst, axes):
    sns.distplot([0.1, 0.1, 0.1, 0.2], bins=b, ax=ax)
    plt.show()

    この挙動がどこから来ているのかと言うと、実はmatplotlibのhistのdensityパラメータをTrueにしていることに由来します。
    seaborn側のコードはこの辺です。
    https://github.com/mwaskom/seaborn/blob/master/seaborn/distributions.py#L219
    matplotlibはこちらを見てください。
    https://matplotlib.org/api/_as_gen/matplotlib.pyplot.hist.html
    > density : bool, optional
    > If True, the first element of the return tuple will be the counts normalized to form a probability density, i.e., the area (or integral) under the histogram will sum to 1. This is achieved by dividing the count by the number of observations times the bin width and not dividing by the total number of observations.

    こんなのも実行してみるとわかりやすいかもしれません。
    import numpy as np
    import matplotlib.pyplot as plt

    scales = list(range(5))
    fig, axes = plt.subplots(ncols=5)

    for s, ax in zip(scales, axes):
    ax.hist(np.array([0.1, 0.1, 0.2, 0.2])*10**s, density=True)
    plt.show()

    直感的には値の区間が広くなればそれだけ「近い値は稀になる」ので、こういう仕様も理にかなっているのかもしれません。個人的にはかなり違和感がありますが。
    hist_kws={"density":False}とするとヒストグラムの方は観測度数がそのまま出てきます。PDFの方もできればなんとかしたいんですが、すぐに見つからなかったので提示できません。左右にy軸のあるグラフにして右は度数、左はPDFの値に対応づけられたらいいんですが、seabornがやってくれないので、そういうのがほしかったら自分で書くべきかもしれません。

    キャンセル

  • 2020/02/10 18:55

    コメント&ソースリンクもありがとうございます。
    デフォルト動作(kdeがTrue)だとnorm_histもTrueになるんですね。
    たしかにPDFとヒストグラムを重ねて表示するにはこちらのほうが自然なのでしょう。
    ただx軸(データ値)のスケールにしたがいy軸のスケールが変わってくるという動きは
    distplotの動きを知らすに&とりあえず結果から推論する私には、かなり混乱させるものでした(笑
    そもそもPDFとヒストグラムを重ねて表示させることに、ちょっと無理がある(PDFだけでいいじゃん)ような気がします。

    > 左右にy軸のあるグラフにして右は度数、左はPDFの値に対応づけられたらいいんですが
    同感です。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.77%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る