Mathematik für Biologiestudierende¶

Wintersemester 2025/26

Dreikönigstag 2026

© 2026 Prof. Dr. Rüdiger W. Braun

Wiederholung (interaktiv)¶

Gehen Sie auf die Website

  • https://pingo.coactum.de

und geben Sie folgende Zugangsnummer ein

  • 670719

oder scannen Sie den QR-Code

QR-Code

Themen heute¶

  • Data Snooping
  • Multiple Vergleiche
  • Bonferroni-Korrektur
  • Gruppenvergleiche
  • ANOVA
  • Bonferroni-Holm-Korrektur
In [1]:
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

Data Snooping¶

  • "Snooping" = "Schnüffeln"
  • Data Snooping bedeutet, dass man den Test für dieselben Daten rechnet, die man auch für die Formulierung der Hypothese benutzt hat
  • Jemand stellt fest, dass in einer Stadt von 50 Neugeborenen 35 weiblich sind.
  • Wir machen den entsprechenden Binomialtest
In [2]:
stats.binomtest(35, 50)
Out[2]:
BinomTestResult(k=35, n=50, alternative='two-sided', statistic=0.7, pvalue=0.006600447966810918)
  • Die Nullhypothese, dass die Wahrscheinlichkeiten von Jungs- und Mädchengeburten gleich sind, kann abgelehnt werden. Der $p$-Wert beträgt $0.0066$
  • Was bedeutet das?

Szenario 1¶

  • Theorie: Bekanntlich werden die Babies von den Weißstörchen gebracht. Eine neue Theorie besagt, dass Schwarzstörche das zwar auch tun, aber nicht im korrekten Geschlechterverhältnis.
  • Vorgehen: Die Forscher wählen daher einen Ort mit einer großen Population an Schwarzstörchen aus und untersuchen dort das Verhältnis von Mädchen- zu Jungsgeburten.
  • Bewertung: Wenn dort das Zahlenverhältnis 35:15 beträgt, ist die Theorie zum Signifikanzniveau $p = 0.0066$ bestätigt.

Szenario 2¶

  • Theorie: Keine. Man will bloss mal gucken.
  • Vorgehen: In 100 Gemeinden mit mehr als 50 Geburten wird das Verhältnis zwischen Mädchen- und Jungsgeburten untersucht. Tatsächlich findet sich eine Gemeinde mit dem Zahlenverhältnis 35:15
  • Bewertung: Das ist Data Snooping, weil die Hypothese aus denselben Daten generiert worden ist, mit denen anschließend der Test gerechnet wird.

Gefahren des Data Snooping¶

  • Wir haben ein Experiment, welches nur mit einer Wahrscheinlichkeit $ p = 0.0066 $ gelingt.
  • Wir wiederholen dieses Experiment 100 mal.

Mit welcher Wahrscheinlichkeit gelingt das Experiment in mindestens einem dieser Fälle?

Das ist eine Wiederholung eines ja/nein-Experiments mit folgenden Daten

  • Erfolgswahrscheinlichkeit im Einzelfall $p=0.0066$
  • Stichpropenumfang $n=100$

Zugehörige Binomialverteilung

In [3]:
P = stats.binom(100, 0.0066)

Wahrscheinlichkeit, dass es keinen einzigen Erfolg gibt

In [4]:
p0 = P.pmf(0)
p0
Out[4]:
0.5157218904013275

Wahrscheinlichkeit, dass es mindestens einen Erfolg gibt

In [5]:
1 - p0
Out[5]:
0.48427810959867246

Fazit¶

  • Ein einzelnes Ereignis, welches mit Wahrscheinlichkeit 0.0066 auftritt, ist überraschend
  • Wenn eines von hundert Ereignissen, die jedes einzeln mit Wahrscheinlichkeit 0.0066 auftreten, tatsächlich eintritt, dann ist das nicht überraschend

Multiple Vergleiche¶

Möglichkeiten für korrektes Vorgehen

  • Versuchsplanung
    • Pilotversuch
  • statistische Verfahren
    • Bonferroni-Korrektur
    • Bonferroni-Holm-Korrektur
    • False Discovery Rate

Bonferroni-Korrektur¶

  • Wenn man simultan $n$ Vergleiche durchführt, dann schreibt die Bonferroni-Korrektur vor, dass man jeden einzelnen Vergleich zum Signifikanzniveau $\frac\alpha n$ durchführt, um insgesamt das Signifikanzniveau $\alpha$ zu erreichen
  • Im Beispiel der Schwarzstörche hätte für jeden Einzeltest das Signifikanzniveau $\frac{0.05}{100} = 0.0005$ gewählt werden müssen

Bonferroni-Holm-Korrektur¶

erkläre ich, nachdem ich die Bonferroni-Korrektur für die ANOVA vorgemacht habe

False Discovery Rate¶

  • Beispiel Bilddaten: 20 Magnetresonanztomographie-Aufnahmen (MRT) von gesunden Gehirnen und 20 MRT-Aufnahmen von erkrankten Gehirnen, wobei die Gehirne auf einen Standardatlas normalisiert werden
  • In dem Standardatlas werden alle Voxel (3D-Pixel) markiert, bei denen der Eisengehalt der Gruppe der Erkrankten signifikant über dem der Gruppe der Gesunden liegt

Wenn der Bildausschnit $100 mm \times 100mm \times 100mm$ beträgt und die Auflösung des MRT bei 1.5mm liegt, dann haben wir

In [6]:
100/1.5
Out[6]:
66.66666666666667

Voxel für eine Seitenlänge, also insgesamt

In [7]:
66 * 66 * 66
Out[7]:
287496

d.h. knapp 290000 Voxel. Damit ist eine Bonferroni-Korrektur undenkbar.

  • Alternative: False Discovery Rate
  • FDR von 1% sagt: im Schnitt sind nur 1% aller markierten Voxel falsch
  • Zum Vergleich: Ein multipler Test zum Signifikanzniveau 5% sagt: Die Wahrscheinlichkeit, dass auch nur ein einziges Voxel zu Unrecht markiert ist, beträgt höchstens 5%

Gruppenvergleiche¶

  • An fünf verschiedenen Messstellen wurde die Konzentration eines Schadstoffs gemessen
  • Hat die Messstelle einen Einfluss auf die Konzentration?
  • Wir könnten die Konzentrationen an je zwei Messstellen vergleichen
  • Das sind $\binom52 = 10$ Vergleiche
  • Wir müssen dann entsprechend Bonferroni korrigieren
  • nicht optimal
  • Solche Gruppenvergleiche sind das Einsatzgebiet der ANOVA und ihrer Varianten
  • ANOVA = Analysis of Variance

ANOVA¶

  • ANOVA: Analysis of Variance
  • Ziel: Vergleich bei Vorliegen von mehr als zwei Gruppen

Welche Daten hat man?

  • Eine Population
  • Eine Zielvariable. Das ist die kontinuierliche Größe, die gemessen wird.
  • Einen Faktor. Das ist eine kategorielle (also qualitative oder diskrete quantitative) Größen, von der man nachweisen will, dass sie die Zielvariable beeinflusst
  • Alle Mitglieder der Population, bei denen der Faktor denselben Wert hat, bilden eine Gruppe
  • die Zielvariable muss normalverteilt sein
  • innerhalb aller Gruppen ist die Varianz der Zielvariablen dieselbe
  • Wenn man nur zwei Gruppen hat, dann macht man einen unverbundenen, zweiseitigen t-Test

Beispiel Schadstoffkonzentration¶

  • An fünf verschiedenen Messstellen wurde die Konzentration eines Schadstoffs gemessen
  • Hat die Messstelle einen Einfluss auf die Konzentration?
  • Die Messstelle ist der Faktor
  • Die Konzentration ist die Zielvariable
  • es gibt fünf Gruppen, eine für jede Messstelle
In [8]:
u_schad = "https://www.math.uni-duesseldorf.de/~braun/bio2324/data/schadstoffe.csv"
schadstoff = pd.read_csv(u_schad, index_col=0)
schadstoff.head()
Out[8]:
Messstelle Konzentration
0 5 0.000867
1 3 0.000490
2 1 0.000589
3 1 0.000950
4 4 0.001152
In [9]:
sns.displot(data=schadstoff, x='Konzentration', col='Messstelle');
No description has been provided for this image

Wir müssen die Gruppen mit pandas trennen

In [10]:
g1 = schadstoff[schadstoff.Messstelle==1].Konzentration
g1
Out[10]:
2     0.000589
3     0.000950
13    0.001301
14    0.001605
18    0.000927
22    0.001250
28    0.000965
33    0.000669
41    0.000712
42    0.001019
45    0.000780
54    0.001306
61    0.001006
64    0.001057
65    0.000381
70    0.000919
74    0.001323
Name: Konzentration, dtype: float64
In [11]:
g2 = schadstoff[schadstoff.Messstelle==2].Konzentration
g3 = schadstoff[schadstoff.Messstelle==3].Konzentration
g4 = schadstoff[schadstoff.Messstelle==4].Konzentration
g5 = schadstoff[schadstoff.Messstelle==5].Konzentration

wenn Sie das machen, schauen Sie sich die einzelnen Tabellen an, ob sie so aussehen wie gewünscht

In [12]:
g4.head()
Out[12]:
4     0.001152
5     0.001318
6     0.000849
8     0.000982
23    0.000505
Name: Konzentration, dtype: float64
In [13]:
res = stats.f_oneway(g1, g2, g3, g4, g5)
res
Out[13]:
F_onewayResult(statistic=0.8666121588849811, pvalue=0.48807057520065544)
  • es wurde eine one way ANOVA gerechnet, also eine mit nur einem Faktor
  • Der p-Wert ist 0.5
  • Die Messstelle hat keinen Einfluss auf die Konzentration

Welche Verteilung benutzt dieser Test?

  • Die F-Verteilung dient zum Vergleich zweier Varianzen
  • Sie hat zwei Parameter:
    • bei der einfaktoriellen ANOVA ist der erste Parameter gleich $g-1$, wenn $g$ die Anzahl der Gruppen ist
    • und der zweite ist $n-g$, wenn $n$ der Stichprobenumfang ist
  • In scipy.stats wird sie zur Verfügung gestellt durch P = stats.f(g-1, n-g)
  • Wenn t die Teststatistik der ANOVA ist, dann ist 1 - P.cdf(t) der p-Wert
  • Im Beispiel: $g=5$, $n=80$
In [14]:
P = stats.f(4, 75)
1 - P.cdf(res.statistic)
Out[14]:
0.48807057520065544

Zum Vergleich

In [15]:
res.pvalue
Out[15]:
0.48807057520065544

Haben unterschiedliche Pinguinarten unterschiedliche Schnabellängen?¶

In [16]:
pingus = sns.load_dataset("penguins") 
pingus.head()
Out[16]:
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
In [17]:
sns.displot(pingus, x='bill_length_mm', hue='species', multiple='stack');
No description has been provided for this image
In [18]:
pingus.species.value_counts()
Out[18]:
species
Adelie       152
Gentoo       124
Chinstrap     68
Name: count, dtype: int64
In [19]:
gA = pingus[pingus.species=='Adelie'].bill_length_mm
gA.head()
Out[19]:
0    39.1
1    39.5
2    40.3
3     NaN
4    36.7
Name: bill_length_mm, dtype: float64
In [20]:
gG = pingus[pingus.species=='Gentoo'].bill_length_mm
gC = pingus[pingus.species=='Chinstrap'].bill_length_mm
In [21]:
stats.f_oneway(gA, gG, gC)
Out[21]:
F_onewayResult(statistic=nan, pvalue=nan)
  • Was ist das Problem?
  • Es gibt Einträge ohne Werte
  • Wir entfernen alle Pinguine aus der Liste, bei denen irgendein Wert fehlt
In [22]:
pingus_alle_daten = pingus.dropna()
In [23]:
gA = pingus_alle_daten[pingus_alle_daten.species=='Adelie'].bill_length_mm
gG = pingus_alle_daten[pingus_alle_daten.species=='Gentoo'].bill_length_mm
gC = pingus_alle_daten[pingus_alle_daten.species=='Chinstrap'].bill_length_mm
In [24]:
res = stats.f_oneway(gA, gG, gC)
res
Out[24]:
F_onewayResult(statistic=397.2994374128277, pvalue=1.3809842053153047e-88)

Also haben unterschiedliche Pinguinarten unterschiedliche Schnabellängen

Wir hätten auch drei t-Tests rechnen können

In [25]:
r1 = stats.ttest_ind(gA, gG)
r1
Out[25]:
TtestResult(statistic=-24.66879239628207, pvalue=2.2112060856021175e-70, df=263.0)
In [26]:
r2 = stats.ttest_ind(gA, gC)
r2
Out[26]:
TtestResult(statistic=-23.562058327794357, pvalue=3.988191872307172e-61, df=212.0)
In [27]:
r3 = stats.ttest_ind(gG, gC)
r3
Out[27]:
TtestResult(statistic=-2.608098387774673, pvalue=0.00984830289764215, df=185.0)
  • Das ist multiples Testen, muss also korrigiert werden
  • Drei Tests gerechnet
  • Gewünscht: $\alpha=0.01$
  • Bonferroni-Korrektur: Jeden einzelnen Test zu $\frac\alpha3 = 0.003333$ auswerten
  • Zu $\alpha=0.01$ werden Unterschiede in den Schnabellängen zwischen Adelie- und Eselspinguinen und zwischen Adelie- und Zügelpinguinen gefunden
  • Der Unterschied zwischen Esels- und Zügelpinguinen ist nicht signifikant

Automatische Paarvergleiche¶

  • Wir wollen zwischen je zwei Gruppen die Paarvergleiche zu Fuß ausrechnen und Bonferroni-korrigieren
  • Dieser Prozess ist implementiert
In [28]:
from statsmodels.sandbox.stats.multicomp import MultiComparison

Achtung: Hier wird irgendwann der Bestandteil sandbox überflüssig

In [29]:
muc = MultiComparison(pingus.dropna().bill_length_mm, pingus.dropna().species)

muc = MultiComparison(daten_liste, gruppen_liste)

  • das erste Element von daten_liste gehört zur ersten Gruppe in Gruppenliste
  • das zweite Element von daten_liste gehört zur zweiten Gruppe in Gruppenliste
  • usw.

muc.allpairtest(test, alpha, method)

  • Paarvergleiche zwischen allen Paaren von Gruppen aus der Gruppenliste, mit der muc angelegt wurde
  • test ist der einzusetzende Test
  • alpha das Signifikanzniveau (Standardwert ist 0.05)
  • method die Korrekturmethode für das multiple Testen, für uns relevant:
    • bonferroni: Bonferroni
    • holm: Bonferroni-Holm
In [30]:
res = muc.allpairtest(stats.ttest_ind, alpha=0.01, method='bonferroni')
res[0]
Out[30]:
Test Multiple Comparison ttest_ind FWER=0.01 method=bonferroni alphacSidak=0.00, alphacBonf=0.003
group1 group2 stat pval pval_corr reject
Adelie Chinstrap -23.5621 0.0 0.0 True
Adelie Gentoo -24.6688 0.0 0.0 True
Chinstrap Gentoo 2.6081 0.0098 0.0295 False

Nur zwei der Paarunterschiede sind signifikant, wenn Bonferroni korrigiert wird

Dasselbe mit Bonferroni-Holm

In [31]:
res = muc.allpairtest(stats.ttest_ind, alpha=0.01, method='holm')
res[0]
Out[31]:
Test Multiple Comparison ttest_ind FWER=0.01 method=holm alphacSidak=0.00, alphacBonf=0.003
group1 group2 stat pval pval_corr reject
Adelie Chinstrap -23.5621 0.0 0.0 True
Adelie Gentoo -24.6688 0.0 0.0 True
Chinstrap Gentoo 2.6081 0.0098 0.0098 True
  • Mit Bonferroni-Holm-Korrektur sind in diesem Beispiel alle Paarunterschiede signifikant.
  • Es hängt von der Fächerkultur ab, ob Bonferroni-Holm akzeptiert wird

Bonferroni-Holm¶

  • n multiple Vergleiche werden durchgeführt
  • Die p-Werte werden der Größe nach geordnet
  • der kleinste p-Wert muss signifikant zu $\frac\alpha n$ sein
  • deswegen wird der $n$-fache $p$-Wert angezeigt
  • der zweitkleinste zu $\frac\alpha{n-1}$
  • der $n-1$-fache $p$-Wert wird angezeigt
  • drittkleinste zu $\frac\alpha{n-2}$
  • usw.
  • der größte zum Niveau $\alpha$

Bei den Pinguinen¶

  • der kleinste p-Wert ist der des Vergleichs zwischen Adelie- und Eselspinguinen
  • er ist unkorrigiert gleich 2.211e-70
  • es gibt 3 Paarvergleiche, also ist der Bonferroni-korrigierte Wert gleich 3 * 2.311e-70 = 6.933e-70
  • die Bonferroni-Holm Korrektur führt zum selben Wert
  • der zweitkleinste p-Wert ist der des Vergleichs zwischen Adelie- und Zügelpinguinen
  • er ist unkorrigiert gleich 3.988e-61
  • also mit Bonferroni-Korrektur gleich 3 * 3.988e-61 = 1.196e-60
  • nach Bonferroni-Holm ist er gleich 2 * 3.988e-61 = 7.976e-61
  • der größte p-Wert ist der des Vergleichs zwischen Esels- und Zügelpinguinen
  • er ist unkorrigiert gleich 0.009848
  • also mit Bonferroni-Korrektur gleich 3 * 0.009848 = 0.02954
  • nach Bonferroni-Holm ist er gleich 0.009848