Abschnitt 1
Hypothesentests mit Python
In der modernen Praxis berechnet man Teststatistiken und
p-Werte nur noch selten von Hand oder mit einer Tabelle im
Umschlag einer Formelsammlung. Wir nutzen hierzu Python und
die Bibliothek scipy.stats, welche eine
riesige Palette an Werkzeugen für Zufallsgrössen und
statistische Tests bereitstellt. Für Berechnungen mit
Feldern von Messwerten verwenden wir numpy
(als np importiert) und zur Visualisierung
die bereits eingeführte Bibliothek
matplotlib.pyplot (als plt
importiert).
Vorteil gegenüber Formelsammlung/Taschenrechner
Python rechnet direkt mit den rohen Messdaten (einer Liste von Zahlen), nicht nur mit bereits zusammengefassten Kennzahlen wie n, x̄ oder s. Ausserdem lassen sich beliebige Situationen simulieren, für die es gar keine fertige Formel gibt (siehe Simulation statt Tabelle).
Auch die Code-Beispiele im Theorieteil stehen in einer Werkbank: Sie lassen sich frei verändern und mit Ausführen starten, genau wie bei den Übungen weiter unten. Erzeugt der Code einen Plot, erscheint er unter der Ausgabe.
scipy.stats für Zufallsgrössen
Für jede bekannte Verteilung stellt scipy.stats
ein Objekt zur Verfügung. Die wichtigsten für uns sind:
stats.binom(n, p)- Binomialverteilung (diskret)
stats.norm(loc, scale)-
Normalverteilung mit μ (
loc) und σ (scale) (stetig) stats.uniform(loc, scale)-
Uniformverteilung auf [
loc,loc + scale] (stetig) stats.expon(scale)-
Exponentialverteilung mit λ = 1 /
scale(stetig)
Diese Objekte verfügen über Methoden zur Berechnung von Wahrscheinlichkeiten:
pmf(k)- Wahrscheinlichkeitsfunktion P(X = k) (nur für diskrete Verteilungen).
pdf(x)- Dichtefunktion f(x) (nur für stetige Verteilungen).
cdf(x)- Verteilungsfunktion F(x) = P(X ≤ x).
ppf(q)- Prozentpunktfunktion (Inverse CDF). Liefert das x, für das P(X ≤ x) = q gilt.
rvs(size)- Generiert Zufallswerte (Random Variates) dieser Verteilung.
Beispiel. Nutzung von Zufallsgrössen-Objekten in SciPy
Vom Datensatz zur Entscheidung: der p-Wert
Bisher kannten wir alle Parameter (zum Beispiel p bei der Binomialverteilung) bereits im Voraus. In der Praxis liegen uns aber meistens nur Messdaten vor (eine Stichprobe), aus denen wir schliessen wollen, ob eine Vermutung (H₁) haltbar ist. Genau dafür sind statistische Tests da.
Definition: der p-Wert
Der p-Wert ist die Wahrscheinlichkeit, unter der Annahme der Nullhypothese H₀ ein mindestens so extremes Testergebnis wie das tatsächlich beobachtete zu erhalten.
Es gilt die fundamentale Entscheidungsregel für ein vorgegebenes Signifikanzniveau α:
Falls p-Wert ≤ α ⟹ H₀ ablehnen (Entscheidung für H₁)
Falls p-Wert > α ⟹ H₀ beibehalten (nicht genügend Evidenz für H₁)
Jede Funktion in scipy.stats, die einen
Test durchführt, übernimmt für uns genau die Rolle,
die früher eine Tabelle oder eine von Hand
hergeleitete Formel hatte: Sie bestimmt die Verteilung
der Teststatistik unter H₀ und liest daraus den
p-Wert ab. Wir müssen diese Verteilung nicht mehr
selbst kennen.
Statistische Tests in Python
Wir betrachten nun fünf fundamentale Hypothesentests und implementieren diese mit konkreten, anwendungsnahen Beispielen in Python.
1. Der Einstichproben-t-Test (1-sample t-test)
Der t-Test prüft, ob der Erwartungswert μ einer (annähernd normalverteilten) Grundgesamtheit von einem vorgegebenen Sollwert μ₀ abweicht.
Ein Abfüller behauptet, dass seine Kaffeepackungen im Mittel μ₀ = 500 g Kaffee enthalten. Wir wiegen 10 Packungen und erhalten die Stichprobe: [495, 496, 502, 498, 492, 497, 501, 493, 495, 499]. Weicht das Gewicht signifikant nach unten ab?
H₀: μ ≥ 500 g vs. H₁: μ < 500 g (einseitiger Test)
Beispiel. Einstichproben-t-Test in Python
Der berechnete p-Wert ist ca. 0.0055. Da 0.0055 ≤ 0.05, lehnen wir die Nullhypothese ab. Die Kaffeepackungen sind signifikant unterfüllt.
2. Der Zweistichproben-t-Test (2-sample t-test)
Er vergleicht die Mittelwerte zweier unabhängiger Gruppen auf signifikante Unterschiede.
Wir wollen wissen, ob Tomatenpflanzen mit einem neuen Dünger (Gruppe A) höher wachsen als mit dem Standarddünger (Gruppe B).
H₀: μA ≤ μB vs. H₁: μA > μB (einseitig)
Beispiel. Zweistichproben-t-Test mit Boxplot-Visualisierung
3. Der gepaarte t-Test (paired t-test)
Manchmal werden dieselben Objekte oder Personen zweimal gemessen (zum Beispiel vorher/nachher). Dann sind die beiden Stichproben nicht unabhängig, sondern paarweise verbunden. In diesem Fall betrachtet man einfach die Differenzen der Paare und testet, ob deren Erwartungswert von 0 abweicht.
Zehn Schülerinnen und Schüler absolvieren einen Vokabeltest, üben danach vier Wochen mit einer Lern-App und schreiben denselben Test nochmals. Hat die App etwas gebracht?
H₀: μnachher ≤ μvorher vs. H₁: μnachher > μvorher (einseitig)
Beispiel. Gepaarter t-Test in Python
Der p-Wert ist verschwindend klein (p ≈ 0.000003). Die Verbesserung ist hochsignifikant.
4. Der Binomialtest
Diskrete Variante für dichotome Variablen (Erfolg/Misserfolg). Er testet die hypothetische Erfolgswahrscheinlichkeit p bei n Versuchen exakt, ganz ohne Normalapproximation.
Ein fehlerhafter Münzwurfautomat liefert bei n=20 Würfen genau 17 Mal Kopf. Weicht die Münze signifikant von einer fairen Münze (p₀=0.5) ab?
H₀: p = 0.5 vs. H₁: p ≠ 0.5 (zweiseitig)
Beispiel. Binomialtest mit grafischer Darstellung der Extrembereiche
Der p-Wert beträgt hier ca. 0.0026. Da 0.0026 ≤ 0.05, ist die Münze signifikant unfair.
5. Der Chi-Quadrat-Test
Damit lassen sich zwei verwandte Fragestellungen beantworten:
-
Anpassungstest (goodness of fit):
Passt eine beobachtete Häufigkeitsverteilung über
mehrere Kategorien zu einer erwarteten
(theoretischen) Verteilung? (
stats.chisquare) -
Unabhängigkeitstest: Sind zwei kategoriale
Variablen (z. B. Geschlecht und bevorzugtes Schulfach)
unabhängig voneinander?
(
stats.chi2_contingency)
Wir bestimmen, ob die Wahl des Lieblingshaustiers (Hund/Katze) vom Wohnort (Stadt/Land) abhängt. Die erhobenen Daten werden in einer Kontingenztabelle erfasst:
| Hund | Katze | |
|---|---|---|
| Stadtbewohner | 45 | 55 |
| Landbewohner | 70 | 30 |
H₀: Wohnort und Haustierwahl sind unabhängig
voneinander.
H₁: Wohnort und Haustierwahl hängen voneinander ab.
Beispiel. Chi-Quadrat-Unabhängigkeitstest in Python
Der p-Wert ist extrem klein (p ≈ 0.0006). Wir lehnen H₀ ab. Die Haustierpräferenz unterscheidet sich signifikant zwischen Stadt und Land.
Vertrauensintervalle mit Python
Mit scipy.stats können wir ein
Konfidenzintervall direkt aus Rohdaten berechnen, ohne
die zugrundeliegende Formel selbst auswendig zu kennen.
Eine besonders vielseitige, moderne Methode dazu ist das
Bootstrap-Verfahren: Aus der vorhandenen
Stichprobe wird (mit Zurücklegen) sehr oft neu gezogen,
jedes Mal der Mittelwert berechnet, und aus der so
entstandenen Verteilung von Mittelwerten das Intervall
abgelesen.
Eine Umfrage bei 15 Jugendlichen ergibt die folgende tägliche Bildschirmzeit (in Minuten): [210, 245, 190, 260, 300, 175, 220, 255, 230, 205, 280, 195, 240, 265, 215]. Wie lautet ein 95%-Konfidenzintervall für die durchschnittliche Bildschirmzeit aller Jugendlichen?
Beispiel. Bootstrap-Konfidenzintervall in Python
Wir erhalten ungefähr das Intervall [216, 250] Minuten. Bei wiederholter Umfrage würden etwa 95% der so berechneten Intervalle die wahre durchschnittliche Bildschirmzeit aller Jugendlichen enthalten.
Simulation statt Tabelle: p-Werte durch Zufallsexperimente schätzen
Der eigentliche Sinn eines p-Wertes ist einfach: «Wie wahrscheinlich ist ein mindestens so extremes Resultat, wenn H₀ stimmt?» Diese Frage lässt sich immer beantworten, indem man H₀ am Computer sehr oft simuliert und nachzählt — ganz ohne Formel, Tabelle oder eingebaute Testfunktion. Das ist besonders nützlich, wenn (wie oft im echten Leben) keine passende Testfunktion existiert.
Wir überprüfen dies am Münzbeispiel von oben (n=20, k=17, p₀=0.5) nochmals nach:
Beispiel. Schätzung eines p-Wertes durch Simulation (Monte-Carlo-Methode)
Der simulierte p-Wert (≈ 0.0025) stimmt (bis auf den
unvermeidlichen Simulationsfehler) mit dem exakten Wert
von stats.binomtest (≈ 0.0026) überein.
Diese Technik heisst Monte-Carlo-Simulation und
funktioniert für praktisch jede denkbare Fragestellung,
solange man weiss, wie man H₀ simulieren kann.
Genau aus diesem Grund braucht man in der Praxis keine gedruckten Wahrscheinlichkeitstabellen mehr: Statt eine Verteilung nachzuschlagen, lässt man den Computer die Situation unter H₀ nachbauen (simulieren oder exakt berechnen) und liest den p-Wert direkt ab.
Checkliste: Welchen Test verwende ich?
- Ein Mittelwert soll mit einem festen Sollwert verglichen werden → Einstichproben-t-Test (
ttest_1samp). - Zwei unabhängige Gruppen sollen bezüglich ihres Mittelwerts verglichen werden → Zweistichproben-t-Test (
ttest_ind). - Dieselben Objekte/Personen wurden zweimal gemessen (vorher/nachher) → Gepaarter t-Test (
ttest_rel). - Ein Anteil (eine Erfolgswahrscheinlichkeit p) soll mit einem festen Sollwert verglichen werden → Binomialtest (
binomtest). - Beobachtete Häufigkeiten mehrerer Kategorien sollen mit erwarteten Häufigkeiten verglichen werden → Chi-Quadrat-Anpassungstest (
chisquare). - Zwei kategoriale Variablen sollen auf Zusammenhang untersucht werden (Kontingenztabelle) → Chi-Quadrat-Unabhängigkeitstest (
chi2_contingency). - Für nichts davon gibt es eine passende Funktion, oder man möchte die Bedeutung des p-Wertes nachvollziehen → Simulation/Monte-Carlo-Methode (siehe oben).
Übungen
Lösen Sie die folgenden Aufgaben mit Python (also mit
scipy.stats/numpy), nicht von
Hand und nicht mit einer Wahrscheinlichkeitstabelle. Geben
Sie jeweils Nullhypothese und Alternativhypothese an
(auf Papier oder im Kommentar Ihres Codes), lesen Sie den
p-Wert ab und formulieren Sie einen Antwortsatz.
Ein Mobile-Game verkauft «Beutekisten» mit je einem von 5 Charakter-Skins. Der Hersteller wirbt damit, dass alle 5 Skins gleich wahrscheinlich (je 20%) seien. Eine Spielerin öffnet 200 Kisten und erhält folgende Häufigkeiten:
| Skin | A | B | C | D | E |
|---|---|---|---|---|---|
| Häufigkeit | 50 | 38 | 45 | 42 | 25 |
Ist diese Verteilung mit der Werbeaussage des Herstellers verträglich (Niveau α = 5%)?
Hinweis: stats.chisquare.
Lösungsvorschlag
Wir testen mit einem Chi-Quadrat-Anpassungstest, ob die beobachteten Häufigkeiten zu der behaupteten Gleichverteilung (je 40 von 200) passen:
H₀: Alle 5 Skins sind gleich wahrscheinlich. vs. H₁: Die Skins sind nicht gleich wahrscheinlich.
Lösungsvorschlag (lootbox.py)
Dies liefert p-Wert ≈ 0.0624. Da 0.0624 > 0.05, behalten wir H₀ bei: Die Abweichungen sind (knapp) noch mit einer Gleichverteilung verträglich, auch wenn Skin E auffällig selten vorkam.
Ein Online-Shop testet zwei Layouts seiner Bestellseite gegeneinander (A/B-Test). Von 1000 Besuchenden mit Layout A schliessen 120 einen Kauf ab, von 1000 Besuchenden mit Layout B sind es 158. Unterscheiden sich die beiden Layouts bezüglich der Kaufwahrscheinlichkeit signifikant (Niveau α = 5%)?
Hinweis: Stellen Sie die Daten als
2×2-Kontingenztabelle (gekauft/nicht gekauft) dar
und benutzen Sie stats.chi2_contingency.
Lösungsvorschlag
Wir bilden die Kontingenztabelle der Käufe und Nicht-Käufe pro Layout und testen auf Unabhängigkeit von Layout und Kaufentscheid. Die erste Zeile steht für Layout A, die zweite für Layout B; die erste Spalte zählt die Käufe, die zweite die Nicht-Käufe:
H₀: Layout und Kaufentscheid sind unabhängig (gleiche Conversion-Rate). vs. H₁: Sie hängen zusammen.
Lösungsvorschlag (abtest.py)
Dies liefert p-Wert ≈ 0.0140. Da 0.0140 ≤ 0.05, lehnen wir H₀ ab: Layout B hat eine signifikant höhere Conversion-Rate (15.8% statt 12.0%).
Eine Fitness-App wirbt damit, dass Nutzende, die dem eingebauten Trainingsplan folgen, im Durchschnitt mindestens 10'000 Schritte pro Tag erreichen. Der Konsumentenschutz vermutet, dass die tatsächliche durchschnittliche Schrittzahl darunter liegt, und lässt 10 zufällig ausgewählte Nutzende ihre über eine Woche gemittelte tägliche Schrittzahl protokollieren:
[9800, 9200, 10100, 8950, 9400, 9600, 9300, 10050, 8800, 9500]
Ist die Werbeaussage auf dem Niveau α = 5% widerlegt?
Hinweis: einseitiger
Einstichproben-t-Test,
stats.ttest_1samp.
Lösungsvorschlag
Wir setzen H₀: μ ≥ 10'000 (Werbeaussage stimmt) vs. H₁: μ < 10'000 (Werbeaussage ist übertrieben).
Lösungsvorschlag (schritte.py)
Der Mittelwert der Stichprobe beträgt 9470 Schritte, und es ergibt sich p-Wert ≈ 0.0019. Da 0.0019 ≤ 0.05, lehnen wir H₀ ab: Die durchschnittliche Schrittzahl liegt signifikant unter den beworbenen 10'000.
Zehn Schülerinnen und Schüler schreiben einen Vokabeltest (max. 20 Punkte), üben danach vier Wochen lang mit einer neuen Lern-App und schreiben denselben Test nochmals:
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
|---|---|---|---|---|---|---|---|---|---|---|
| Vorher | 11 | 9 | 14 | 8 | 12 | 10 | 13 | 9 | 11 | 10 |
| Nachher | 13 | 12 | 15 | 10 | 14 | 12 | 14 | 12 | 13 | 12 |
Hat die App auf dem Niveau α = 5% zu einer signifikanten Verbesserung geführt?
Hinweis: Dieselben Personen werden
zweimal gemessen – gepaarter t-Test,
stats.ttest_rel.
Lösungsvorschlag
Da dieselben Schülerinnen und Schüler zweimal getestet wurden, sind die Stichproben nicht unabhängig. Wir verwenden einen gepaarten, einseitigen t-Test: H₀: μnachher ≤ μvorher vs. H₁: μnachher > μvorher.
Lösungsvorschlag (vokabelapp.py)
Der p-Wert ist verschwindend klein (p ≈ 0.0000028). Wir lehnen H₀ klar ab: Die Verbesserung um durchschnittlich 2 Punkte ist hochsignifikant.
In einem E-Sport-Verein wird vermutet, dass Spielerinnen und Spieler mit der Maus schneller reagieren als solche mit dem Controller. Man misst die Reaktionszeit (in Millisekunden, tiefer ist schneller) bei je 8 unabhängigen Personen:
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
|---|---|---|---|---|---|---|---|---|
| Maus | 210 | 198 | 205 | 215 | 190 | 200 | 208 | 195 |
| Controller | 230 | 225 | 240 | 220 | 235 | 245 | 228 | 232 |
Bestätigt sich die Vermutung auf dem Niveau α = 5%?
Hinweis: zwei unabhängige Gruppen –
Zweistichproben-t-Test, stats.ttest_ind.
Lösungsvorschlag
Die beiden Gruppen bestehen aus unterschiedlichen Personen und sind daher unabhängig. Da eine tiefere Reaktionszeit «schneller» bedeutet, lautet der einseitige Test: H₀: μMaus ≥ μController vs. H₁: μMaus < μController.
Lösungsvorschlag (esport.py)
Es ergibt sich p-Wert ≈ 0.0000026. Wir lehnen H₀ klar ab: Mit der Maus wird hochsignifikant schneller reagiert als mit dem Controller.
Eine Umfrage bei 15 zufällig ausgewählten Jugendlichen ergibt die folgende tägliche Bildschirmzeit am Smartphone (in Minuten):
[210, 245, 190, 260, 300, 175, 220, 255, 230, 205, 280, 195, 240, 265, 215]
Bestimmen Sie mit dem Bootstrap-Verfahren ein 95%-Konfidenzintervall für die durchschnittliche Bildschirmzeit aller Jugendlichen.
Hinweis: stats.bootstrap.
Lösungsvorschlag
Wir ziehen sehr oft (mit Zurücklegen) neue Stichproben aus den vorhandenen Daten, berechnen jeweils den Mittelwert und lesen aus der so entstandenen Verteilung das Konfidenzintervall ab:
Lösungsvorschlag (bildschirmzeit.py)
Der Mittelwert der Stichprobe beträgt 232.3 Minuten, das 95%-Konfidenzintervall liegt bei ungefähr [216, 250] Minuten. Wegen der zufälligen Ziehung beim Bootstrap-Verfahren kann Ihr Resultat leicht abweichen.
Eine App simuliert einen «fairen» Münzwurf. Bei
100 Würfen erhält man 62 Mal «Kopf». Schätzen Sie
den zweiseitigen p-Wert für H₀: p=0.5 durch
eine eigene Simulation (nicht mit
stats.binomtest!) und vergleichen Sie
anschliessend mit dem exakten Wert von
stats.binomtest.
Hinweis: Simulieren Sie sehr oft 100
faire Münzwürfe mit
rng.binomial(100, 0.5, size=...) und
zählen Sie, wie oft das Ergebnis mindestens so
weit von 50 entfernt ist wie die beobachteten 62.
Lösungsvorschlag
Statt eine fertige Testfunktion zu benutzen, bauen wir die Definition des p-Wertes direkt als Simulation nach:
Lösungsvorschlag (montecarlo.py)
Beide Methoden liefern (bis auf den kleinen Simulationsfehler) denselben Wert: p ≈ 0.021. Da 0.021 ≤ 0.05, ist die Münze auf dem 5%-Niveau signifikant unfair. Die Simulation zeigt, dass man für diese Frage gar keine eingebaute Testfunktion braucht, sondern nur die Definition des p-Wertes direkt nachbauen muss.