Kurtosis-Test#

Die Kurtosis-Testfunktion scipy.stats.kurtosistest prüft die Nullhypothese, dass die Kurtosis der Population, aus der die Stichprobe gezogen wurde, die der Normalverteilung ist.

Angenommen, wir möchten anhand von Messungen ableiten, ob die Gewichte erwachsener männlicher Personen in einer medizinischen Studie nicht normalverteilt sind [1]. Die Gewichte (in lbs) sind im unten stehenden Array x aufgeführt.

import numpy as np
x = np.array([148, 154, 158, 160, 161, 162, 166, 170, 182, 195, 236])

Der Kurtosis-Test aus [2] beginnt mit der Berechnung einer Statistik basierend auf der Stichproben-Kurtosis (Überschuss-/Fisher-Kurtosis).

from scipy import stats
res = stats.kurtosistest(x)
res.statistic
np.float64(2.3048235214240873)

(Der Test warnt, dass unsere Stichprobe zu wenige Beobachtungen enthält, um den Test durchzuführen. Wir werden darauf am Ende des Beispiels zurückkommen.) Da Normalverteilungen per Definition eine Überschuss-Kurtosis von Null haben, ist die Größe dieser Statistik für Stichproben aus einer Normalverteilung tendenziell gering.

Der Test wird durchgeführt, indem der beobachtete Wert der Statistik mit der Nullverteilung verglichen wird: der Verteilung von Statistikwerten, die unter der Nullhypothese abgeleitet wurden, dass die Gewichte aus einer Normalverteilung stammen.

Für diesen Test ist die Nullverteilung der Statistik für sehr große Stichproben die Standardnormalverteilung.

import matplotlib.pyplot as plt
dist = stats.norm()
kt_val = np.linspace(-5, 5, 100)
pdf = dist.pdf(kt_val)
fig, ax = plt.subplots(figsize=(8, 5))

def kt_plot(ax):  # we'll reuse this
    ax.plot(kt_val, pdf)
    ax.set_title("Kurtosis Test Null Distribution")
    ax.set_xlabel("statistic")
    ax.set_ylabel("probability density")

kt_plot(ax)
plt.show()
../../_images/eaa3f2daf98c78ee228ee56af8e21e17dee3a83be8f0ef9cb5d72d673f8abd42.png

Der Vergleich wird durch den p-Wert quantifiziert: dem Anteil der Werte in der Nullverteilung, die so extrem oder extremer als der beobachtete Wert der Statistik sind. Bei einem zweiseitigen Test, bei dem die Statistik positiv ist, gelten sowohl die Elemente der Nullverteilung, die größer als die beobachtete Statistik sind, als auch die Elemente der Nullverteilung, die kleiner als das Negative der beobachteten Statistik sind, als "extremer".

fig, ax = plt.subplots(figsize=(8, 5))
kt_plot(ax)
pvalue = dist.cdf(-res.statistic) + dist.sf(res.statistic)
annotation = (f'p-value={pvalue:.3f}\n(shaded area)')
props = dict(facecolor='black', width=1, headwidth=5, headlength=8)
_ = ax.annotate(annotation, (3, 0.005), (3.25, 0.02), arrowprops=props)
i = kt_val >= res.statistic
ax.fill_between(kt_val[i], y1=0, y2=pdf[i], color='C0')
i = kt_val <= -res.statistic
ax.fill_between(kt_val[i], y1=0, y2=pdf[i], color='C0')
ax.set_xlim(-5, 5)
ax.set_ylim(0, 0.1)
plt.show()
../../_images/eb067e29bda0f7f214cb892db7f51faae297d28cd86f92c92eb8c814ef3fc522.png
res.pvalue
np.float64(0.0211764592113868)

Wenn der p-Wert "klein" ist - d.h. wenn die Wahrscheinlichkeit gering ist, aus einer normalverteilten Population Daten zu ziehen, die einen derart extremen Wert der Statistik erzeugen -, kann dies als Hinweis gegen die Nullhypothese zugunsten der Alternativhypothese gewertet werden: die Gewichte wurden nicht aus einer Normalverteilung gezogen. Beachten Sie, dass

  • Das Umgekehrte gilt nicht; d.h. der Test wird nicht verwendet, um Beweise für die Nullhypothese zu liefern.

  • Der Schwellenwert für Werte, die als "klein" gelten, ist eine Entscheidung, die getroffen werden sollte, bevor die Daten analysiert werden [3], unter Berücksichtigung der Risiken sowohl von falsch positiven Ergebnissen (fälschliche Ablehnung der Nullhypothese) als auch von falsch negativen Ergebnissen (Nichtablehnung einer falschen Nullhypothese).

Beachten Sie, dass die Standardnormalverteilung eine asymptotische Annäherung der Nullverteilung liefert; sie ist nur für Stichproben mit vielen Beobachtungen genau. Das ist der Grund, warum wir zu Beginn des Beispiels eine Warnung erhalten haben; unsere Stichprobe ist recht klein. In diesem Fall kann scipy.stats.monte_carlo_test eine genauere, wenn auch stochastische, Annäherung an den exakten p-Wert liefern.

def statistic(x, axis):
    # get just the skewtest statistic; ignore the p-value
    return stats.kurtosistest(x, axis=axis).statistic
res = stats.monte_carlo_test(x, stats.norm.rvs, statistic)
fig, ax = plt.subplots(figsize=(8, 5))
kt_plot(ax)
ax.hist(res.null_distribution, np.linspace(-5, 5, 50),
        density=True)
ax.legend(['asymptotic approximation\n(many observations)',
           'Monte Carlo approximation\n(11 observations)'])
plt.show()
../../_images/942f27fb2a44c66c7dd587e99eb27a8de3562f99683d8dd55b05b37e03634529.png
res.pvalue
np.float64(0.0246)

Darüber hinaus können, trotz ihrer stochastischen Natur, auf diese Weise berechnete p-Werte verwendet werden, um die Rate falscher Ablehnungen der Nullhypothese genau zu steuern [4].

Referenzen#