web-dev-qa-db-ja.com

python scipyでコルモゴロフスミルノフテストを実装する

正規性をテストしたいN個のデータセットがあります。私はscipy.statsに kstest function があることを知っていますが、それを使用する方法と結果を解釈する方法の例はありません。ここで私にアドバイスをくれる人はいますか?

ドキュメントによると、kstestを使用すると、2つの数値、KS検定統計Dとp値が返されます。p値が有意水準(たとえば5%)より大きい場合、データが与えられた分布からのものであるという仮説を棄却することはできません。

正規分布から10000個のサンプルを抽出し、ガウス性をテストすることでテストを実行すると、次のようになります。

import numpy as np
from scipy.stats import kstest

mu,sigma = 0.07, 0.89
kstest(np.random.normal(mu,sigma,10000),'norm')

次の出力が得られます。

(0.04957880905196102、8.9249710700788814e-22)

P値は5%未満です。これは、データが正規分布しているという仮説を棄却できることを意味します。しかし、サンプルは正規分布から抽出されました!

誰かがここでの矛盾を理解して私に説明できますか?

(正規性のテストでは、mu = 0およびsigma = 1と想定していますか?その場合、データがガウス分布しているが、muとsigmaが異なることをテストするにはどうすればよいですか?)

25
Hooloovoo

データはmu = 0.07およびsigma = 0.89で生成されました。このデータを、平均が0で標準偏差が1の正規分布に対してテストしています。

帰無仮説(H0)は、データがサンプルである分布が、平均0、標準偏差1の標準正規分布に等しいことです。

小さいp値は、確率p値でDと同じ大きさの検定統計量が期待されることを示しています。

言い換えると、(p値が〜8.9e-22の場合)H0はtrueです。

平均と標準偏差が一致しないため、これは合理的です。

結果を以下と比較します。

In [22]: import numpy as np
In [23]: import scipy.stats as stats
In [24]: stats.kstest(np.random.normal(0,1,10000),'norm')
Out[24]: (0.007038739782416259, 0.70477679457831155)

データがガウス分布であることをテストするには、データをシフトして再スケーリングし、平均が0で標準偏差が1の正規分布になるようにします。

data=np.random.normal(mu,sigma,10000)
normed_data=(data-mu)/sigma
print(stats.kstest(normed_data,'norm'))
# (0.0085805670733036798, 0.45316245879609179)

警告:ser333700に感謝します (別名scipy開発者 Josef Perktold )) musigmaがわからない場合、パラメーターを推定するとp値が無効になります。

import numpy as np
import scipy.stats as stats

mu = 0.3
sigma = 5

num_tests = 10**5
num_rejects = 0
alpha = 0.05
for i in xrange(num_tests):
    data = np.random.normal(mu, sigma, 10000)
    # normed_data = (data - mu) / sigma    # this is okay
    # 4915/100000 = 0.05 rejects at rejection level 0.05 (as expected)
    normed_data = (data - data.mean()) / data.std()    # this is NOT okay
    # 20/100000 = 0.00 rejects at rejection level 0.05 (not expected)
    D, pval = stats.kstest(normed_data, 'norm')
    if pval < alpha:
        num_rejects += 1
ratio = float(num_rejects) / num_tests
print('{}/{} = {:.2f} rejects at rejection level {}'.format(
    num_rejects, num_tests, ratio, alpha))     

プリント

20/100000 = 0.00 rejects at rejection level 0.05 (not expected)

これはstats.kstestサンプルがサンプルの平均と標準偏差を使用して正規化されている場合、予想される帰無仮説の数を拒否しない場合があります

normed_data = (data - data.mean()) / data.std()    # this is NOT okay
25
unutbu

Unutbuの答えの更新:

場所とスケールにのみ依存し、形状パラメーターを持たない分布の場合、いくつかの適合度検定統計の分布は、場所とスケールの値に依存しません。分布は非標準ですが、基になる分布の任意の場所と規模で表にして使用できます。

推定された位置とスケールをもつ正規分布のコルモゴロフ-スミルノフ検定は、 リリーフォース検定 とも呼ばれます。

現在、statsmodelsで利用可能であり、関連する決定範囲の近似p値があります。

>>> import numpy as np
>>> mu,sigma = 0.07, 0.89
>>> x = np.random.normal(mu, sigma, 10000)
>>> import statsmodels.api as sm
>>> sm.stats.lilliefors(x)
(0.0055267411213540951, 0.66190841161592895)

ほとんどのモンテカルロ研究は、アンダーソン・ダーリング検定がコルモゴロフ・スミルノフ検定よりも強力であることを示しています。これは、臨界値を持つscipy.stats、および近似p値を持つstatsmodelsで利用できます。

>>> sm.stats.normal_ad(x)
(0.23016468240712129, 0.80657628536145665)

どちらの検定も、サンプルが正規分布であるという帰無仮説を棄却しません。問題のkstestは、サンプルが標準正規分布しているという帰無仮説を棄却します。

12
Josef

また、「データが正規分布から抽出されたという帰無仮説を検定する」シャピロ-ウィルク検定の使用を検討することもできます。 scipyにも実装されています:

http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.shapiro.html

データを関数に直接渡す必要があります。

import scipy

W, p = scipy.stats.shapiro(dataset)
print("Shapiro-Wilk test statistic, W:", W, "\n", "p-value:", p)

これは次のようなものを返します:

 Shapiro-Wilk test statistic, W: 0.7761164903640747 
 p-value: 6.317247641091492e-37

P << 0.01(または、0.05、必要に応じて-それは問題ではない)の場合、これらのデータが正規分布から抽出されたという帰無仮説を棄却する十分な理由があります。

3
D. Betchkal

@unutbuによる回答の補足として、kstestでテスト分布の分布パラメーターを提供することもできます。変数からいくつかのサンプルがあり(そしてそれらにdataxという名前を付けた)、それらのサンプルが対数正規、均一、または正規から得られない可能性があるかどうかを確認したいとします。 scipy統計の場合、各分布の入力パラメーターの取得方法は少し異なります。これで、kstestの「args」(タプルまたはシーケンス)のおかげで、テストするscipy.statsディストリビューションの引数を指定できます。

:)どちらかの方法で実行したい場合に備えて、2つのサンプルのテストを使用するオプションも追加しました。

import numpy as np
from math import sqrt
from scipy.stats import kstest, ks_2samp, lognorm
import scipy.stats

def KSSeveralDists(data,dists_and_args,samplesFromDists=100,twosampleKS=True):
    returnable={}
    for dist in dists_and_args:
        try:
            if twosampleKS:
                try:
                    loc=dists_and_args[dist][0]
                    scale=dists_and_args[dist][1]
                    expression='scipy.stats.'+dist+'.rvs(loc=loc,scale=scale,size=samplesFromDists)'
                    sampledDist=eval(expression)
                except:
                    sc=dists_and_args[dist][0]
                    loc=dists_and_args[dist][1]
                    scale=dists_and_args[dist][2]
                    expression='scipy.stats.'+dist+'.rvs(sc,loc=loc,scale=scale,size=samplesFromDists)'
                    sampledDist=eval(expression)
                D,p=ks_2samp(data,sampledDist)
            else:
                D,p=kstest(data,dist,N=samplesFromDists,args=dists_and_args[dist])
        except:
            continue
        returnable[dist]={'KS':D,'p-value':p}
    return returnable

a=lambda m,std: m-std*sqrt(12.)/2.
b=lambda m,std: m+std*sqrt(12.)/2.
sz=2000

sc=0.5 #shape 
datax=lognorm.rvs(sc,loc=0.,scale=1.,size=sz)
normalargs=(datax.mean(),datax.std())

#suppose these are the parameters you wanted to pass for each distribution
dists_and_args={'norm':normalargs,
               'uniform':(a(*normalargs),b(*normalargs)),
               'lognorm':[0.5,0.,1.]
              }
print "two sample KS:"
print KSSeveralDists(datax,dists_and_args,samplesFromDists=sz,twosampleKS=True)
print "one sample KS:"
print KSSeveralDists(datax,dists_and_args,samplesFromDists=sz,twosampleKS=False)

次のような出力が得られます。

2つのサンプルKS:{'lognorm':{'KS':0.023499999999999965、 'p-value':0.63384188886455217}、 'norm':{'KS':0.10600000000000004、 'p-value':2.918766666723155e-10}、 'ユニフォーム':{' KS ':0.15300000000000002、' p-value ':6.443660021191129e-21}}

1つのサンプルKS:{'lognorm':{'KS':0.01763415915126032、 'p-value':0.56275820961065193}、 'norm':{'KS':0.10792612430093562、 'p-value':0.0}、 'uniform':{ 'KS':0.14910036159697559、 'p値':0.0}}

注:scipy.statsの一様分布の場合、aとbはa = locおよびb = loc +スケールと見なされます( ドキュメント を参照)。

1