Skewness-Test#
Diese Funktion testet die Nullhypothese, dass die Schiefe der Grundgesamtheit, aus der die Stichprobe gezogen wurde, dieselbe ist wie die einer entsprechenden Normalverteilung.
Angenommen, wir möchten anhand von Messungen ableiten, ob die Gewichte erwachsener Männer in einer medizinischen Studie nicht normalverteilt sind [1]. Die Gewichte (lbs) sind im untenstehenden Array x aufgezeichnet.
import numpy as np
x = np.array([148, 154, 158, 160, 161, 162, 166, 170, 182, 195, 236])
Der Schiefe-Test scipy.stats.skewtest aus [2] beginnt mit der Berechnung einer Statistik basierend auf der Stichprobenschiefe.
from scipy import stats
res = stats.skewtest(x)
res.statistic
np.float64(2.7788579769903414)
Da Normalverteilungen eine Schiefe von Null haben, ist die Größe dieser Statistik für Stichproben, die aus einer Normalverteilung gezogen wurden, 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()
st_val = np.linspace(-5, 5, 100)
pdf = dist.pdf(st_val)
fig, ax = plt.subplots(figsize=(8, 5))
def st_plot(ax): # we'll reuse this
ax.plot(st_val, pdf)
ax.set_title("Skew Test Null Distribution")
ax.set_xlabel("statistic")
ax.set_ylabel("probability density")
st_plot(ax)
plt.show()
Der Vergleich wird durch den p-Wert quantifiziert: der Anteil der Werte in der Nullverteilung, die so extrem oder extremer sind als der beobachtete Wert der Statistik. Bei einem zweiseitigen Test werden sowohl Elemente der Nullverteilung, die größer als die beobachtete Statistik sind, als auch Elemente der Nullverteilung, die kleiner als das Negative der beobachteten Statistik sind, als "extremer" betrachtet.
fig, ax = plt.subplots(figsize=(8, 5))
st_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 = st_val >= res.statistic
ax.fill_between(st_val[i], y1=0, y2=pdf[i], color='C0')
i = st_val <= -res.statistic
ax.fill_between(st_val[i], y1=0, y2=pdf[i], color='C0')
ax.set_xlim(-5, 5)
ax.set_ylim(0, 0.1)
plt.show()
res.pvalue
np.float64(0.005455036974740185)
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 Wahl, die getroffen werden sollte, bevor die Daten analysiert werden [3], wobei die Risiken von sowohl falsch positiven Ergebnissen (fälschliches Ablehnen der Nullhypothese) als auch falsch negativen Ergebnissen (Nichtablehnen einer falschen Nullhypothese) berücksichtigt werden.
Beachten Sie, dass die Standardnormalverteilung eine asymptotische Annäherung der Nullverteilung liefert; sie ist nur für Stichproben mit vielen Beobachtungen genau. Für kleine Stichproben wie unsere kann scipy.stats.monte_carlo_test eine genauere, wenn auch stochastische, Annäherung des exakten p-Wertes liefern.
def statistic(x, axis):
# get just the skewtest statistic; ignore the p-value
return stats.skewtest(x, axis=axis).statistic
res = stats.monte_carlo_test(x, stats.norm.rvs, statistic)
fig, ax = plt.subplots(figsize=(8, 5))
st_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()
res.pvalue
np.float64(0.005)
In diesem Fall stimmen die asymptotische Annäherung und die Monte-Carlo-Annäherung auch für unsere kleine Stichprobe ziemlich gut überein.