Mathematik für Biologiestudierende¶
Wintersemester 2024/25
- Oktober 2024
© 2024 Prof. Dr. Rüdiger W. Braun
Python¶
python
,seaborn
,jupyter
: Was ist das?
python
ist eine Programmiersprache- Wir wollen aber gar nicht programmieren
- Wir wollen fertige Bibliotheken anwenden
- Für Naturwissenschaftlerinnen und Naturwissenschaftler sind das
pandas
,numpy
,seaborn
und noch einige andere - Das sind für alle Teilnehmenden dieses Kurses dieselben Bibliotheken
- In der Frage der Oberfläche gibt es verschiedene Möglichkeiten, je nach persönlichem Geschmack
- Hier benutze ich
jupyter notebook
auf meinem Rechner - Eine andere Möglichkeit ist https://colab.research.google.com
- Dort läuft ebenfalls
jupyter
Installation auf eigenem Rechner¶
- weil wir mehr als nur
python
brauchen, gibt es auf https://www.math.uni-duesseldorf.de/~internet/bio2425/software.html eine Installationsanweisung
Warum nicht Excel?¶
- Hilft uns nur auf der ersten Hälfte unseres Weges
- Ist schon bei mittelkomplizierten Fragestellungen nicht wirklich einfacher
- Spreadsheets sind sehr, sehr schwer zu verifizieren
- Daten und Programmlogik sind nicht getrennt: Fehlerquelle
Wofür Excel gut ist:
- Einmalrechnungen, die nicht reproduziert weden müssen
- gutes Eingabeformat
import pandas as pd
Die Bibiothek zur Bearbeitung von Tabellen heißt pandas
Einlesen von Daten¶
sns.load_dataset()
für die Beispieldatensätze, die bei seaborn mitgeliefert werde
für echte Datensätze
pd.read_csv()
Daten im csv-Formatpd.read_excel()
Daten in einem der Excel-Formate
Einlesen von der Festplatte
iris = pd.read_csv("iris/iris_extended.csv")
https://www.kaggle.com/datasets/samybaladram/iris-dataset-extended/
© Samy Baladram, CC-BY-4.0
Um die Datei von der Adresse bei kaggle herunterzuladen, ist eine Anmeldung erforderlich. Alternativ erhalten Sie sie über den folgenden Aufruf:
iris = pd.read_csv("https://github.com/Ruediger-Braun/mfb2425/raw/refs/heads/master/iris/iris_extended.csv")
iris
species | elevation | soil_type | sepal_length | sepal_width | petal_length | petal_width | sepal_area | petal_area | sepal_aspect_ratio | ... | sepal_to_petal_length_ratio | sepal_to_petal_width_ratio | sepal_petal_length_diff | sepal_petal_width_diff | petal_curvature_mm | petal_texture_trichomes_per_mm2 | leaf_area_cm2 | sepal_area_sqrt | petal_area_sqrt | area_ratios | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | setosa | 161.8 | sandy | 5.16 | 3.41 | 1.64 | 0.26 | 17.5956 | 0.4264 | 1.513196 | ... | 3.146341 | 13.115385 | 3.52 | 3.15 | 5.33 | 18.33 | 53.21 | 4.194711 | 0.652993 | 41.265478 |
1 | setosa | 291.4 | clay | 5.48 | 4.05 | 1.53 | 0.37 | 22.1940 | 0.5661 | 1.353086 | ... | 3.581699 | 10.945946 | 3.95 | 3.68 | 5.90 | 20.45 | 52.53 | 4.711051 | 0.752396 | 39.205087 |
2 | setosa | 144.3 | sandy | 5.10 | 2.80 | 1.47 | 0.38 | 14.2800 | 0.5586 | 1.821429 | ... | 3.469388 | 7.368421 | 3.63 | 2.42 | 5.66 | 24.62 | 50.25 | 3.778889 | 0.747395 | 25.563910 |
3 | setosa | 114.6 | clay | 4.64 | 3.44 | 1.53 | 0.17 | 15.9616 | 0.2601 | 1.348837 | ... | 3.032680 | 20.235294 | 3.11 | 3.27 | 4.51 | 22.91 | 50.85 | 3.995197 | 0.510000 | 61.367166 |
4 | setosa | 110.9 | loamy | 4.85 | 2.87 | 1.23 | 0.26 | 13.9195 | 0.3198 | 1.689895 | ... | 3.943089 | 11.038462 | 3.62 | 2.61 | 4.03 | 21.56 | 40.57 | 3.730885 | 0.565509 | 43.525641 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
1195 | virginica | 268.8 | loamy | 5.36 | 2.51 | 5.16 | 1.93 | 13.4536 | 9.9588 | 2.135458 | ... | 1.038760 | 1.300518 | 0.20 | 0.58 | 11.61 | 11.52 | 59.46 | 3.667915 | 3.155757 | 1.350926 |
1196 | virginica | 125.4 | clay | 7.49 | 3.06 | 7.68 | 2.17 | 22.9194 | 16.6656 | 2.447712 | ... | 0.975260 | 1.410138 | -0.19 | 0.89 | 13.85 | 6.99 | 77.12 | 4.787421 | 4.082352 | 1.375252 |
1197 | virginica | 73.6 | clay | 6.79 | 3.25 | 4.72 | 2.26 | 22.0675 | 10.6672 | 2.089231 | ... | 1.438559 | 1.438053 | 2.07 | 0.99 | 13.13 | 9.16 | 74.39 | 4.697606 | 3.266068 | 2.068725 |
1198 | virginica | 239.6 | sandy | 6.38 | 2.24 | 5.30 | 1.71 | 14.2912 | 9.0630 | 2.848214 | ... | 1.203774 | 1.309942 | 1.08 | 0.53 | 11.01 | 6.46 | 73.90 | 3.780370 | 3.010482 | 1.576873 |
1199 | virginica | 201.5 | loamy | 5.16 | 3.20 | 5.64 | 1.43 | 16.5120 | 8.0652 | 1.612500 | ... | 0.914894 | 2.237762 | -0.48 | 1.77 | 12.09 | 8.59 | 64.31 | 4.063496 | 2.839930 | 2.047314 |
1200 rows × 21 columns
Einlesen aus dem Internet
fish = pd.read_csv("https://stats.idre.ucla.edu/stat/data/fish.csv")
fish
nofish | livebait | camper | persons | child | xb | zg | count | |
---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 1 | 0 | -0.896315 | 3.050405 | 0 |
1 | 0 | 1 | 1 | 1 | 0 | -0.558345 | 1.746149 | 0 |
2 | 0 | 1 | 0 | 1 | 0 | -0.401731 | 0.279939 | 0 |
3 | 0 | 1 | 1 | 2 | 1 | -0.956298 | -0.601526 | 0 |
4 | 0 | 1 | 0 | 1 | 0 | 0.436891 | 0.527709 | 1 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
245 | 1 | 1 | 1 | 2 | 0 | -0.755236 | 2.324209 | 0 |
246 | 0 | 1 | 1 | 4 | 3 | 1.794859 | -5.625944 | 0 |
247 | 0 | 1 | 1 | 2 | 1 | -0.392649 | 0.677275 | 0 |
248 | 1 | 1 | 1 | 3 | 2 | 1.374641 | -2.595630 | 0 |
249 | 1 | 1 | 1 | 2 | 1 | 0.828834 | -1.457115 | 0 |
250 rows × 8 columns
Ein DataFrame
ist eine Tabelle, wie etwa in Excel auch.
Auf Spalten greift man mit Indizierung zu:
iris['soil_type']
0 sandy 1 clay 2 sandy 3 clay 4 loamy ... 1195 loamy 1196 clay 1197 clay 1198 sandy 1199 loamy Name: soil_type, Length: 1200, dtype: object
Alternativ auch mit dem Punkt, wenn die Spaltenüberschrift nur aus Buchstaben, Zahlen und Unterstrich besteht
bluetenblattlaenge = iris.petal_length
bluetenblattlaenge
0 1.64 1 1.53 2 1.47 3 1.53 4 1.23 ... 1195 5.16 1196 7.68 1197 4.72 1198 5.30 1199 5.64 Name: petal_length, Length: 1200, dtype: float64
Spalten sind vom Typ pd.Series
Auf Zeilen greift man über den Index zu. In der Regel zählt der Index die Zeilen einfach durch.
iris.loc[1100]
species virginica elevation 108.4 soil_type loamy sepal_length 6.34 sepal_width 3.09 petal_length 5.7 petal_width 2.17 sepal_area 19.5906 petal_area 12.369 sepal_aspect_ratio 2.05178 petal_aspect_ratio 2.626728 sepal_to_petal_length_ratio 1.112281 sepal_to_petal_width_ratio 1.423963 sepal_petal_length_diff 0.64 sepal_petal_width_diff 0.92 petal_curvature_mm 9.46 petal_texture_trichomes_per_mm2 11.5 leaf_area_cm2 73.17 sepal_area_sqrt 4.426127 petal_area_sqrt 3.516959 area_ratios 1.583847 Name: 1100, dtype: object
Deskriptive Statistik¶
Lageparameter¶
Arithmetisches Mittel¶
Das arithmetische Mittel (engl. "mean") ist der Durchschnitt der Messwerte
Formel: Beim Stichprobenumfang $n$ seien $ x_1, x_2, x_3, \dots, x_n $ die Messwerte, dann ist das arithmetische Mittel gleich
$$ \overline x = \frac1n \left( x_1 + x_2 + x_3 + \dots + x_n \right) $$
Man schreibt auch \begin{equation*} \overline x = \frac1n \sum_{j=1}^n x_j \end{equation*}
$\displaystyle \sum$ ist das Summenzeichen.
- Unter dem Zeichen stehen zwei Informationen: Der Name der Zählvariablen (hier $j$) und der Startwert der Zählung (hier $1$).
- Über dem Zeichen steht der Endpunkt der Zählung (hier der Stichprobenumfang $n$)
- Rechts neben dem Zeichen steht, was aufzuzählen ist (hier $x_j$)
Beispiel¶
Bei fünf Mäusen sind die Gewichte 21.3g, 19.8g, 20.4g, 19.0g und 22.7g gemessen worden. Was ist der Mittelwert?
- Die Summe beträgt 103.2g.
- 103.2/5 = 20.64
- Das arithmetische Mittel der Mausgewichte beträgt 20.64g
Das ist mit dem Taschenrechner kein Problem
Trotzdem mal mit pandas
maeuse = pd.DataFrame() # leerer DataFrame
maeuse['Gewicht'] = [21.3, 19.8, 20.4, 19.0, 22.7]
maeuse
Gewicht | |
---|---|
0 | 21.3 |
1 | 19.8 |
2 | 20.4 |
3 | 19.0 |
4 | 22.7 |
maeuse.mean()
Gewicht 20.64 dtype: float64
bluetenblattlaenge.mean()
np.float64(3.80795)
- Je nach verwendeten Versionen der Software sieht man hier entweder
np.float64(3.80795)
oder nur die Zahl - Auf meinem Rechner sehe ich die komplizierte Version, was ich aber wie folgt abschalten kann
import numpy as np
np.set_printoptions(legacy='1.21')
bluetenblattlaenge.mean()
3.80795
- In Lektion 1 hatte ich auf 1.25 umgeschaltet; das mag Colab aber nicht
Median¶
Der Median ist ein Wert mit der Eigenschaft, dass in der Menge der nach Größe geordneten Messwerte gleich viele Daten unterhalb und oberhalb des Medians liegen.
Beispiel: 7 Messwerte
10 | 5 | 4 | 9 | 10 | 1 | 5
Nach Größe anordnen
1 | 4 | 5 | 5 | 9 | 10 | 10
Der Median ist $x_{\text{med}} = 5$
Median für geraden Stichprobenumfang¶
Falls die Anzahl der Daten gerade ist, stehen in der Menge der nach Größe geordneten Messwerte zwei Zahlen in der Mitte.
Der Median $x_{\text{med}}$ ist dann deren arithmetisches Mittel.
10 | 5 | 4 | 9 | 10 | 1 | 5 | 8
Nach Größe anordnen
1 | 4 | 5 | 5 | 8 | 9 | 10 | 10
Der Median ist $x_{\text{med}} = 6.5$
maeuse.median()
Gewicht 20.4 dtype: float64
Bei normalverteilten Daten liegen arithmetisches Mittel und Median nahe beieinander
Einkommensverteilung¶
- Beim Einkommen ziehen die sehr gut verdienenden das arithmetische Mittel nach oben,
- beeinflussen den Median aber kaum
Daten von finanz.de für 2024
- Durchschnittsgehalt: 50.250€
- Median des Gehalts: 43.750€
Robustheit¶
- Ein Ausreißer ist ein Messwert, der weit von fast allen anderen Messwerten entfernt ist. Ausreißer können z.B. von Messfehlern herrühren.
- Eine statistische Größe ist robust, wenn sie unempfindlich gegen Ausreißer ist.
- Das arithmetische Mittel ist nicht robust. Ausreißer gehen genauso ein wie alle anderen.
- Der Median ist robust
Beispiel zur Robustheit¶
maeuse2 = pd.DataFrame()
maeuse2['Gewicht'] = [21.3, 19.8, 20.4, 19.0, 22.7, 287]
maeuse.mean()
Gewicht 20.64 dtype: float64
maeuse2.mean()
Gewicht 65.033333 dtype: float64
maeuse.median()
Gewicht 20.4 dtype: float64
maeuse2.median()
Gewicht 20.85 dtype: float64
Streuungsparameter¶
Empirische Varianz und Stichprobenstreuung¶
- Beim Stichprobenumfang $n$ seien $x_1, x_2, x_3, \dots, x_n$ die Messwerte, dann ist das empirische Varianz gleich \begin{equation*} s^2 = \frac{(x_1-\overline x)^2 + (x_2-\overline x)^2 + (x_3-\overline x)^2 + \dots + (x_n-\overline x)^2}{n-1} \end{equation*} Dabei ist $\overline x$ das arithmetische Mittel
- Die empirische Varianz wird mit $s^2$ bezeichnet.
- Die Zahl $s$ heißt empirische Standardabweichung oder Stichprobenstreuung
- Die Stichprobenstreuung ist also die Quadratwurzel der empirischen Varianz
Konkrete Rechnung¶
Bei fünf Proben wurden die folgenden Massen in [g] gewogen:
Nummer | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
Masse | 1.1 | 1.3 | 1.6 | 1.3 | 2.0 |
$$\displaystyle \sum_{j=1}^5 x_j = 7.3g$$ $$\overline x = \frac{7.3g}5 = 1.46g$$ $$\sum_{j=1}^5 (x_j-\overline x)^2 = 0.4920g^2$$ $$s^2 = \frac{0.4920g^2}4 = 0.1230g^2 \quad\text{und}\quad s = \sqrt{0.1230g^2} = 0.3507g$$
maeuse.var() # empirische Varianz
Gewicht 2.033 dtype: float64
maeuse.std() # Stichprobenstreuung
Gewicht 1.425833 dtype: float64
Stichprobenstreuung vs. Varianz¶
- Der Vorteil der Stichprobenstreuung gegenüber der Varianz ist, dass die Stichprobenstreuung richtig skaliert
- Das bedeutet folgendes: Wenn ich alle Daten mit 1000 multipliziere, dann
- multipliziert sich das arithmetische Mittel mit 1000
- multipliziert sich die Varianz mit 1000000
- multipliziert sich die Stichprobenstreuung mit 1000
- Das bedeutet auch, dass die Stichprobenstreuung in derselben Einheit angegegeben wird wie die Daten
Formeln für die Varianz¶
- Die Definition ohne Pünktchen \begin{equation*} s^2 = \frac1{n-1} \sum_{j=1}^n \left(x_j - \overline x \right)^2 \end{equation*}
- Eine etwas einfachere Formel, deren Richtigkeit leicht nachgerechnet werden kann \begin{equation*} s^2 = \frac1{n-1} \biggr( \Bigr( \sum_{j=1}^n x_j^2 \Bigr) - n {\overline x}^2 \biggr) \end{equation*}
Warum $n - 1$ im Nenner?¶
- $n-1$ ist die Zahl Freiheitsgrade
- Das ist ein heuristisches Konzept:
- erstmal hat man pro Stichprobe einen Freiheitsgrad
- für jeden Wert, den man hilfsweise schätzen muss, wird ein Freiheitsgrad abgezogen
- zur Berechnung der Varianz muss das arithmetische Mittel bestimmt werden: also ein Freiheitgrad Abzug
- Das bedeutet, dass man, wenn man für viele Datensätze die Varianz mit dieser Methode feststellt, im Mittel näher an der wahren Varianz ist, als wenn man den Nenner $n$ benutzt
- Alle großen Software-Pakete machen das so
Software-Pakete:
python
mitpandas
undscipy.stats
R
SPSS
Überprüfung
maeuse.var()
Gewicht 2.033 dtype: float64
Wenn gewünscht, kann man von Nenner $n-1$ zu Nenner $n$ umschalten
maeuse.var(ddof=0)
Gewicht 1.6264 dtype: float64
ddof
= delta degrees of freedom; das ist die Zahl, die man vom Stichprobenumfang abzieht, um die Zahl der Freiheitsgrade zu bekommen
Achten Sie auf Ihren Taschenrechner!
Zusammenfassungen¶
maeuse.describe()
Gewicht | |
---|---|
count | 5.000000 |
mean | 20.640000 |
std | 1.425833 |
min | 19.000000 |
25% | 19.800000 |
50% | 20.400000 |
75% | 21.300000 |
max | 22.700000 |
count
: Anzahlmean
: arithmetisches Mittelstd
: Stichprobenstreuungmin
: kleinster Wert50%
: Median25%
,75%
: Quartilemax
: größter Wert
Funktioniert auch für DataFrames
fish.describe()
nofish | livebait | camper | persons | child | xb | zg | count | |
---|---|---|---|---|---|---|---|---|
count | 250.000000 | 250.000000 | 250.000000 | 250.00000 | 250.000000 | 250.000000 | 250.000000 | 250.000000 |
mean | 0.296000 | 0.864000 | 0.588000 | 2.52800 | 0.684000 | 0.973796 | 0.252323 | 3.296000 |
std | 0.457407 | 0.343476 | 0.493182 | 1.11273 | 0.850315 | 1.440277 | 2.102391 | 11.635028 |
min | 0.000000 | 0.000000 | 0.000000 | 1.00000 | 0.000000 | -3.275050 | -5.625944 | 0.000000 |
25% | 0.000000 | 1.000000 | 0.000000 | 2.00000 | 0.000000 | 0.008267 | -1.252724 | 0.000000 |
50% | 0.000000 | 1.000000 | 1.000000 | 2.00000 | 0.000000 | 0.954550 | 0.605079 | 0.000000 |
75% | 1.000000 | 1.000000 | 1.000000 | 4.00000 | 1.000000 | 1.963855 | 1.993237 | 2.000000 |
max | 1.000000 | 1.000000 | 1.000000 | 4.00000 | 3.000000 | 5.352674 | 4.263185 | 149.000000 |
Quartile¶
- Das Quartile $Q_1$ ist als derjenige Wert definiert, unter dem 25% und über dem 75% der Messwerte liegen
- Das Quartile $Q_3$ ist als derjenige Wert definiert, über dem 25% und unter dem 75% der Messwerte liegen
- Das Quartil $Q_2$ ist der Median
- Bei den Mäusen: $Q_1 = 19.8$ und $Q_3 = 21.3$
Zusammenhang zwischen Quartilen und Quantilen¶
- Allgemeiner ist für $q$ zwischen $0$ und $1$ das $q$-Quantil derjenige Wert, so dass der Anteil der Messwerte unterhalb dieses Wertes gleich $q$ und der Anteil oberhalb gleich $1-q$ ist
- Wenn $q$ in Prozent ausgedrückt wird, dann spricht man auch von Perzentilen
Quartil | Quantil |
---|---|
1. Quartil | 25%-Quantil |
Median | 50%-Quantil |
3. Quartil | 75%-Quantil |
Beispiel
- Leute, die zu den reichsten 1% der Bevölkerung gehören, haben ein Einkommen oberhalb des 99%-Quantils
Interquartilabstand¶
- Der Interquartilabstand ist definiert als \begin{equation*} \text{IQR} = Q_3 - Q_1 \end{equation*}
- Der Interquartilabstand ist ein robustes Maß für die Streuung
Berechnung des Interquartilabstands
bbl = bluetenblattlaenge.describe()
bbl
count 1200.000000 mean 3.807950 std 1.765469 min 0.960000 25% 1.640000 50% 4.300000 75% 5.200000 max 7.840000 Name: petal_length, dtype: float64
bbl["75%"] - bbl["25%"] # Interquartilabstand
3.5600000000000005
(bbl["75%"] - bbl["25%"]).round(6)
3.56
Box-Whisker-Plots¶
import seaborn as sns
sns.boxplot(iris, x="species", y="sepal_length");
Ein Boxplot, auch Box-Whisker-Plot genannt, zeigt von unten nach oben
- untere Einzelpunkte
- unterster Datenpunkt, der kein Einzelpunkt ist
- $Q_1$
- Median
- $Q_3$
- oberster Datenpunkt, der kein Einzelpunkt ist
- obere Datenpunkte
Einzelpunkte sind solche, die weiter als 1.5 Interquartilabstände von $Q_1$ bzw. $Q_3$ weg sind
Manchmal werden die Einzelpunkte als "Ausreißer" bezeichnet. Das kann man aber nicht so einfach gleichsetzen.
sns.boxplot(maeuse2);
Das ist wirklich ein Ausreißer (engl. "outlier")