web-dev-qa-db-ja.com

scipyで信頼区間を取得する正しい方法

データの1次元配列があります。

_a = np.array([1,2,3,4,4,4,5,5,5,5,4,4,4,6,7,8])
_

68%の信頼区間を取得したい(つまり: 1 sigma )。

この回答 の最初のコメントは、これが scipy.stats.norm 関数の_scipy.stats.norm.interval_を使用して達成できることを示しています:

_from scipy import stats
import numpy as np
mean, sigma = np.mean(a), np.std(a)

conf_int = stats.norm.interval(0.68, loc=mean, 
    scale=sigma)
_

しかし this post のコメントは、actual信頼区間を取得する正しい方法は次のとおりであると述べています。

_conf_int = stats.norm.interval(0.68, loc=mean, 
    scale=sigma / np.sqrt(len(a)))
_

つまり、シグマはサンプルサイズの平方根np.sqrt(len(a))で除算されます。

問題は、正しいバージョンはどれですか?

30
Gabriel

平均muおよびstd偏差シグマの正規分布からのa single drawの68%信頼区間は、

_stats.norm.interval(0.68, loc=mu, scale=sigma)
_

Nの平均値の68%信頼区間は、平均muおよびstd偏差シグマの正規分布から引き分けます

_stats.norm.interval(0.68, loc=mu, scale=sigma/sqrt(N))
_

直観的には、これらの式は理にかなっています。ジェリービーンズの瓶を持ち、多数の人々にジェリービーンズの数を推測するように頼むと、各個人はかなり離れているかもしれません-同じ標準偏差sigma-しかし、推測の平均は実際の数を推定する非常にすばらしい仕事をするでしょう、そして、これは1/sqrt(N)の因子による平均収縮の標準偏差に反映されます。


単一のドローに分散_sigma**2_がある場合、 Bienaymé式 により、Nncorrelatedドローの合計には分散_N*sigma**2_があります。 。

平均は、合計をNで割った値に等しくなります。ランダム変数(合計など)に定数を掛けると、分散に2乗した定数が掛けられます。あれは

_Var(cX) = c**2 * Var(X)
_

したがって、平均の分散は等しい

_(variance of the sum)/N**2 = N * sigma**2 / N**2 = sigma**2 / N
_

したがって、平均の標準偏差(分散の平方根)は

_sigma/sqrt(N).
_

これは、分母のsqrt(N)の起源です。


以下は、Tomのコードに基づいたコード例で、上記の主張を示しています。

_import numpy as np
from scipy import stats

N = 10000
a = np.random.normal(0, 1, N)
mean, sigma = a.mean(), a.std(ddof=1)
conf_int_a = stats.norm.interval(0.68, loc=mean, scale=sigma)

print('{:0.2%} of the single draws are in conf_int_a'
      .format(((a >= conf_int_a[0]) & (a < conf_int_a[1])).sum() / float(N)))

M = 1000
b = np.random.normal(0, 1, (N, M)).mean(axis=1)
conf_int_b = stats.norm.interval(0.68, loc=0, scale=1 / np.sqrt(M))
print('{:0.2%} of the means are in conf_int_b'
      .format(((b >= conf_int_b[0]) & (b < conf_int_b[1])).sum() / float(N)))
_

プリント

_68.03% of the single draws are in conf_int_a
67.78% of the means are in conf_int_b
_

サンプルmeanに基づいてsigmaおよびaの推定値で_conf_int_b_を定義する場合、平均が_conf_int_b_に収まらない可能性があることに注意してください目的の周波数。


分布からsampleを取得し、サンプルの平均と標準偏差を計算すると、

_mean, sigma = a.mean(), a.std()
_

これらがpopulation平均と標準偏差に等しいという保証はなく、我々はassuming母集団が正規分布していることに注意してください。これらは自動ではありません!

サンプルを取得し、推定母平均と標準偏差を使用する場合は、使用する必要があります

_mean, sigma = a.mean(), a.std(ddof=1)
_

シグマのこの値は、母集団標準偏差の 不偏推定量 であるためです。

60
unutbu

既知の信頼区間を持つ配列を使用して、メソッドをテストしました。 numpy.random.normal(mu、std、size)は、標準偏差がstdのmuを中心とした配列を返します( the docs で、これはStandard deviation (spread or “width”) of the distribution.として定義されます)。

_from scipy import stats
import numpy as np
from numpy import random
a = random.normal(0,1,10000)
mean, sigma = np.mean(a), np.std(a)
conf_int_a = stats.norm.interval(0.68, loc=mean, scale=sigma)
conf_int_b = stats.norm.interval(0.68, loc=mean, scale=sigma / np.sqrt(len(a)))


conf_int_a
(-1.0011149125527312, 1.0059797764202412)
conf_int_b
(-0.0076030415111100983, 0.012467905378619625)
_

シグマ値は-1〜1である必要があるため、/ np.sqrt(len(a))メソッドは正しくないようです。

編集

上記のコメントには評判がないので、この回答がunutbuの完全な回答とどのように結び付いているかを明確にします。ランダム配列に正規分布を入力すると、合計の68%が平均の1-σ内に収まります。上記の場合、表示されることを確認すると

_b = a[np.where((a>-1)&(a <1))]
len(a)
> 6781
_

または人口の68%が1σ以内に収まります。まあ、約68%。より大きなアレイを使用すると、68%に近づきます(10回の試行では、9は-1と1の間でした)。 1-σはデータの固有の分布であり、データが多いほど解決できるためです。

基本的に、あなたの質問の私の解釈はでした。もしそれらが引き出された分布を記述するために使用したいデータのサンプルがあるなら、そのデータの標準偏差を見つける方法は何ですか? 一方、unutbuの解釈はより多くのように見えます平均を68%の信頼度で配置できる間隔はどれくらいですか? 。つまり、ジェリービーンズについては、私は答えました彼らはどう推測していますかとunutbuは答えましたジェリービーンズについて彼らの推測は何を教えてくれます。

5
Tom

RとGraphPadが信頼区間を計算する方法を確認したところ、サンプルサイズが小さい場合(n)に区間が増加します。たとえば、大きなnと比較して、n = 2の場合は6倍以上です。このコード(shasanの answer に基づく)は、信頼区間と一致します。

_import numpy as np, scipy.stats as st

# returns confidence interval of mean
def confIntMean(a, conf=0.95):
  mean, sem, m = np.mean(a), st.sem(a), st.t.ppf((1+conf)/2., len(a)-1)
  return mean - m*sem, mean + m*sem
_

Rについては、t.test(a)をチェックしました。 GraphPadの 平均の信頼区間 ページには、サンプルサイズの依存関係に関する「ユーザーレベル」情報があります。

ガブリエルの例の出力は次のとおりです。

_In [2]: a = np.array([1,2,3,4,4,4,5,5,5,5,4,4,4,6,7,8])

In [3]: confIntMean(a, 0.68)
Out[3]: (3.9974214366806184, 4.877578563319382)

In [4]: st.norm.interval(0.68, loc=np.mean(a), scale=st.sem(a))
Out[4]: (4.0120010966037407, 4.8629989033962593)
_

confIntMean()st.norm.interval()の間隔の差はここでは比較的小さいことに注意してください。 len(a)== 16は小さすぎません。

5
Ulrich Stern