Mathematik für Biologiestudierende¶
Wintersemester 2025/26
10.12.2025
© 2025 Prof. Dr. Rüdiger W. Braun
Wiederholung (interaktiv)¶
Gehen Sie auf die Website
und geben Sie folgende Zugangsnummer ein
- 670719
oder scannen Sie den QR-Code

Themen heute¶
- Wiederholung t-Test
- p-Wert
- Z-Test
import numpy as np
np.set_printoptions(legacy='1.21')
import seaborn as sns
sns.set_theme()
sns.set_context('talk')
import pandas as pd
from scipy import stats
Anatomie eines Tests¶
(gilt nicht für den Binomialtest)
Teststatistik¶
- berechnet sich aus den Daten
- führt zu einer Zahl, gennant Teststatistik, meist abgekürzt mit $t$
- $t=0$ bedeutet: überhaupt kein Unterschied zwischen Daten und Nullhypothese
Entscheidungsregel¶
- benötigt wird ein Quantil
- zu welcher Verteilung das Quantil bestimmt werden muss, hängt vom Test ab
- zweiseitiger Test: Die Nullhypothese wird abgelehnt, wenn $|t|$ größer als das Quantil ist
- einseitiger oberer Test: Die Nullhypothese wird abgelehnt, wenn $t$ größer als das Quantil ist
- einseitiger unterer Test: Die Nullhypothese wird abgelehnt, wenn $-t$ größer als das Quantil ist
Zusammenfassung: Die Nullhypothese wird abgelehnt, wenn die Teststatistik weit genug weg von der Null ist, wobei bei den einseitigen darauf geachtet werden muss, dass sie "in die richtige Richtung zeigt"
$t$-Tests für Erwartungswerte¶
Wiederholung aus der vorigen Stunde
$t$-Test für unverbundene Stichproben¶
- $x_j$ und $y_j$ seien Realisierungen, also die Daten
- Bestimme arithmetische Mittelwerte $$ \overline x = \frac1{n_1} \sum_{j=1}^{n_1} x_j \text{ und } \overline y = \frac1{n_2} \sum_{j=1}^{n_2} y_j $$
- die Stichprobenstreuungen
$$ s_x = \sqrt{ \frac1{n_1-1} \sum_{j=1}^{n_1} (x_j - \overline x)^2 } \text{ und } s_y = \sqrt{ \frac1{n_2-1} \sum_{j=1}^{n_2} (y_j - \overline y)^2 } $$
- Bestimme die Standardabweichung der gepoolten Stichproben
$$ s_p = \sqrt{ \frac{(n_1-1) \cdot s_x^2 + (n_2 - 1) \cdot s_y^2}{n_1 + n_2 - 2} } $$
- Die Teststatistik ist
$$ t = \frac{\overline x - \overline y}{s_p} \sqrt{\frac{n_1 \cdot n_2}{n_1 + n_2}} $$
Beispiel: Pinguine¶
pingus = sns.load_dataset("penguins")
pingus
| species | island | bill_length_mm | bill_depth_mm | flipper_length_mm | body_mass_g | sex | |
|---|---|---|---|---|---|---|---|
| 0 | Adelie | Torgersen | 39.1 | 18.7 | 181.0 | 3750.0 | Male |
| 1 | Adelie | Torgersen | 39.5 | 17.4 | 186.0 | 3800.0 | Female |
| 2 | Adelie | Torgersen | 40.3 | 18.0 | 195.0 | 3250.0 | Female |
| 3 | Adelie | Torgersen | NaN | NaN | NaN | NaN | NaN |
| 4 | Adelie | Torgersen | 36.7 | 19.3 | 193.0 | 3450.0 | Female |
| ... | ... | ... | ... | ... | ... | ... | ... |
| 339 | Gentoo | Biscoe | NaN | NaN | NaN | NaN | NaN |
| 340 | Gentoo | Biscoe | 46.8 | 14.3 | 215.0 | 4850.0 | Female |
| 341 | Gentoo | Biscoe | 50.4 | 15.7 | 222.0 | 5750.0 | Male |
| 342 | Gentoo | Biscoe | 45.2 | 14.8 | 212.0 | 5200.0 | Female |
| 343 | Gentoo | Biscoe | 49.9 | 16.1 | 213.0 | 5400.0 | Male |
344 rows × 7 columns
Wir möchten zum Signifikanznieveau $\alpha = 0.05$ die folgende Frage beantworten:
Unterscheiden sich die Flügellängen der Adelie-Pinguine je nach Geschlecht?
- Pinguine entweder männlich oder weiblich: unverbundene Stichprobe
- Frage nach Unterschied: zweiseitiger Test
adelie = pingus[pingus.species=="Adelie"]
adelie.head()
| species | island | bill_length_mm | bill_depth_mm | flipper_length_mm | body_mass_g | sex | |
|---|---|---|---|---|---|---|---|
| 0 | Adelie | Torgersen | 39.1 | 18.7 | 181.0 | 3750.0 | Male |
| 1 | Adelie | Torgersen | 39.5 | 17.4 | 186.0 | 3800.0 | Female |
| 2 | Adelie | Torgersen | 40.3 | 18.0 | 195.0 | 3250.0 | Female |
| 3 | Adelie | Torgersen | NaN | NaN | NaN | NaN | NaN |
| 4 | Adelie | Torgersen | 36.7 | 19.3 | 193.0 | 3450.0 | Female |
adelie.sex.value_counts()
sex Male 73 Female 73 Name: count, dtype: int64
am = adelie[adelie.sex=="Male"]
af = adelie[adelie.sex=="Female"]
am.describe()
| bill_length_mm | bill_depth_mm | flipper_length_mm | body_mass_g | |
|---|---|---|---|---|
| count | 73.000000 | 73.000000 | 73.000000 | 73.000000 |
| mean | 40.390411 | 19.072603 | 192.410959 | 4043.493151 |
| std | 2.277131 | 1.018886 | 6.599317 | 346.811553 |
| min | 34.600000 | 17.000000 | 178.000000 | 3325.000000 |
| 25% | 39.000000 | 18.500000 | 189.000000 | 3800.000000 |
| 50% | 40.600000 | 18.900000 | 193.000000 | 4000.000000 |
| 75% | 41.500000 | 19.600000 | 197.000000 | 4300.000000 |
| max | 46.000000 | 21.500000 | 210.000000 | 4775.000000 |
af.describe()
| bill_length_mm | bill_depth_mm | flipper_length_mm | body_mass_g | |
|---|---|---|---|---|
| count | 73.000000 | 73.000000 | 73.000000 | 73.000000 |
| mean | 37.257534 | 17.621918 | 187.794521 | 3368.835616 |
| std | 2.028883 | 0.942993 | 5.595035 | 269.380102 |
| min | 32.100000 | 15.500000 | 172.000000 | 2850.000000 |
| 25% | 35.900000 | 17.000000 | 185.000000 | 3175.000000 |
| 50% | 37.000000 | 17.600000 | 188.000000 | 3400.000000 |
| 75% | 38.800000 | 18.300000 | 191.000000 | 3550.000000 |
| max | 42.200000 | 20.700000 | 202.000000 | 3900.000000 |
Berechnung der Standardabweichung der gepoolten Stichproben
$n_1-1 = 72$ und $n_2-1=72$
zaehler = 72*6.599**2 + 72*5.595**2
nenner = 72 + 72
sp = np.sqrt(zaehler/nenner)
sp
6.117631322660757
faktor = (73*73) / (73+73)
faktor
36.5
Teststatistik
t = (192.4-187.8) / sp * np.sqrt(faktor)
t
4.542772238713508
Signifikanzniveau $\alpha=0.05$
Dann $1-\frac\alpha2 = 0.975$
P = stats.t(72+72)
P.ppf(0.975)
1.9765750658185364
Die Nullhypothese wird abgelehnt: Männliche Adelie-Pinguine haben längere Flügel
Dasselbe mit stats¶
stats.ttest_ind(am.flipper_length_mm, af.flipper_length_mm)
TtestResult(statistic=4.5588666963515765, pvalue=1.08977531716496e-05, df=144.0)
Die Werte der Teststatistik unterscheiden sich etwas.
Woran liegt das?
Rundungsfehler
Hier zur Sicherheit die präzisen Zahlen:
z2 = 72*af.flipper_length_mm.var() + 72*am.flipper_length_mm.var()
sp2 = np.sqrt(z2/nenner)
sp2
6.117818375391429
t = (am.flipper_length_mm.mean() - af.flipper_length_mm.mean()) / sp2 * np.sqrt(faktor)
t
4.5588666963515765
Der p-Wert¶
- Der p-Wert ist das kleinste Signifikanzniveau, zu dem die Nullhypothese noch abgelehnt werden kann
- Um ihn zu bestimmen, benötigen wir die Verteilungsfunktion der Statistik
- wir bezeichnen sie mal kurz mit $F$
- in scipy erhalten wir sie durch
P.cdf()
- eim einseitigen oberen Test ist der p-Wert gleich $1 - F(t)$
- beim einseitigen unteren Test ist der p-Wert gleich $1 - F(-t)$
- beim zweiseitigen Test ist der p-Wert gleich $2(1 - F(|t|)$
Zurück zum Pinguin-Beispiel
2*(1 - P.cdf(abs(t)))
1.0897753171645874e-05
zum Vergleich
stats.ttest_ind(am.flipper_length_mm, af.flipper_length_mm).pvalue
1.08977531716496e-05
Beispiel: Schadstoffkonzentration¶
In der letzten Stunde hatten wir den p-Wert mit scipy.stats bestimmt. Jetzt machen wir das zu Fuß.
u = "https://www.math.uni-duesseldorf.de/~braun/bio2324/data/schadstoffe.csv"
schadstoffe = pd.read_csv(u, index_col=0)
schadstoffe.head()
| Messstelle | Konzentration | |
|---|---|---|
| 0 | 5 | 0.000867 |
| 1 | 3 | 0.000490 |
| 2 | 1 | 0.000589 |
| 3 | 1 | 0.000950 |
| 4 | 4 | 0.001152 |
schadstoffe.describe()
| Messstelle | Konzentration | |
|---|---|---|
| count | 80.000000 | 80.000000 |
| mean | 2.987500 | 0.000905 |
| std | 1.409675 | 0.000341 |
| min | 1.000000 | 0.000061 |
| 25% | 2.000000 | 0.000701 |
| 50% | 3.000000 | 0.000938 |
| 75% | 4.000000 | 0.001158 |
| max | 5.000000 | 0.001605 |
t = (0.000905 - 0.0008) / 0.000341 * np.sqrt(80)
t
2.7541013212607366
- einseitig oberer Test
- 79 Freiheitsgrade
P = stats.t(79)
1 - P.cdf(t)
0.003650577575303182
Zum Vergleich:
res = stats.ttest_rel(schadstoffe.Konzentration, 0.0008, alternative="greater")
res.pvalue
0.0035114445640696246
res.statistic
2.768040010585661
Unterschiede beruhen auf Rundungsfehlern