Mathematik für Biologiestudierende II¶
Sommersemester 2025
17.06.2025
© 2025 Prof. Dr. Rüdiger W. Braun
Themen¶
- Vorhersagen
- Vorhersagen der erwarteten Beobachtungen
- mehrere erklärende Variable
- midparentHeight im Modell von Galton
In [1]:
import numpy as np
np.set_printoptions(legacy='1.21')
import pandas as pd
from scipy import stats
import seaborn as sns
sns.set_theme()
import statsmodels.formula.api as smf
Vorhersagen (prediction)¶
In [2]:
df = pd.read_csv('blutdruckdaten.csv')
df.describe()
Out[2]:
Alter | Blutdruck | Größe | |
---|---|---|---|
count | 30.000000 | 30.000000 | 30.000000 |
mean | 44.800000 | 147.933333 | 176.373333 |
std | 15.202994 | 27.391080 | 5.349504 |
min | 17.000000 | 108.000000 | 165.300000 |
25% | 37.250000 | 125.250000 | 173.350000 |
50% | 45.000000 | 144.500000 | 176.650000 |
75% | 56.000000 | 173.250000 | 179.075000 |
max | 69.000000 | 199.000000 | 189.000000 |
In [3]:
formel = 'Blutdruck ~ Alter'
- In dieser Formel ist
Alter
die erklärende undBlutdruck
die abhängige Variable - Die erklärenden Variablen heißen auch exogen, die abhängigen endogen
- englisch:
- abhängig: dependent oder outcome variable
- erklärend: predictor
In [4]:
modell = smf.ols(formel, df)
res = modell.fit()
res.summary()
Out[4]:
Dep. Variable: | Blutdruck | R-squared: | 0.701 |
---|---|---|---|
Model: | OLS | Adj. R-squared: | 0.690 |
Method: | Least Squares | F-statistic: | 65.54 |
Date: | Tue, 17 Jun 2025 | Prob (F-statistic): | 8.17e-09 |
Time: | 07:58:58 | Log-Likelihood: | -123.27 |
No. Observations: | 30 | AIC: | 250.5 |
Df Residuals: | 28 | BIC: | 253.3 |
Df Model: | 1 | ||
Covariance Type: | nonrobust |
coef | std err | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
Intercept | 80.3697 | 8.798 | 9.135 | 0.000 | 62.348 | 98.391 |
Alter | 1.5081 | 0.186 | 8.096 | 0.000 | 1.127 | 1.890 |
Omnibus: | 2.886 | Durbin-Watson: | 2.401 |
---|---|---|---|
Prob(Omnibus): | 0.236 | Jarque-Bera (JB): | 1.526 |
Skew: | 0.390 | Prob(JB): | 0.466 |
Kurtosis: | 3.782 | Cond. No. | 149. |
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
- Aufgabe aus Lektion 22: Welchen Blutdruck erwartet man für einen 50-jährigen?
- das hatten wir dort zu Fuß ausgerechnet
- In der Sprache der linearen Modelle handelt es sich um eine Vorhersage, engl. prediction
- Wir verwenden nun die Methode
get_prediction
- Dazu müssen die Daten der erklärenden Variablen in einen DataFrame geschrieben werden
In [5]:
anfrage = pd.DataFrame()
anfrage['Alter'] = [50]
# rechte Seite ist auch ein array, auch wenn nur ein Wert berechnet werden soll
anfrage
Out[5]:
Alter | |
---|---|
0 | 50 |
In [6]:
res.get_prediction(anfrage).summary_frame()
Out[6]:
mean | mean_se | mean_ci_lower | mean_ci_upper | obs_ci_lower | obs_ci_upper | |
---|---|---|---|---|---|---|
0 | 155.775537 | 2.948193 | 149.736438 | 161.814636 | 123.956163 | 187.59491 |
- Man erwartet einen Blutdruck von 155.77 mm Hg
- Das stimmt bis auf Rundungsfehler mit dem Ergebnis aus Lektion 22 überein
mean
: Wert, der im Mittel zu erwarten istmean_se
: Standardabweichung fürmean
- die vier anderen Werte sind untere bzw. obere Grenzen für Konfidenzintervalle
mean_ci
ist das Konfidenzintervall für den mittleren zu erwartenden Wertobs_ci
ist das Konfidentintervall für den individuell zu beobachtenden Wert (engl: "observed")
mean_ci_lower
undmean_ci_upper
begrenzen die blaue Kurve imregplot
In [7]:
ax = sns.regplot(df, x='Alter', y='Blutdruck')
ax;
Vorhersagen der erwarteten Beobachtungen¶
obs_ci_lower
undobs_ci_upper
begrenzen einen Bereich, der 95% der erwarteten Beobachtungen enthält
- wir malen den mal hin
In [8]:
anfrage = pd.DataFrame()
anfrage['Alter'] = np.arange(18, 70)
anfrage.head()
Out[8]:
Alter | |
---|---|
0 | 18 |
1 | 19 |
2 | 20 |
3 | 21 |
4 | 22 |
In [9]:
vorhersage = res.get_prediction(anfrage).summary_frame()
vorhersage.head()
Out[9]:
mean | mean_se | mean_ci_lower | mean_ci_upper | obs_ci_lower | obs_ci_upper | |
---|---|---|---|---|---|---|
0 | 107.515824 | 5.716491 | 95.806124 | 119.225524 | 74.152388 | 140.879261 |
1 | 109.023940 | 5.554540 | 97.645982 | 120.401899 | 75.775486 | 142.272395 |
2 | 110.532056 | 5.394160 | 99.482621 | 121.581491 | 77.394588 | 143.669525 |
3 | 112.040172 | 5.235496 | 101.315746 | 122.764599 | 79.009654 | 145.070690 |
4 | 113.548288 | 5.078708 | 103.145027 | 123.951550 | 80.620646 | 146.475931 |
In [10]:
sns.lineplot(x=anfrage.Alter, y=vorhersage.obs_ci_lower, ax = ax, color='orange')
sns.lineplot(x=anfrage.Alter, y=vorhersage.obs_ci_upper, ax=ax, color='orange')
Out[10]:
<Axes: xlabel='Alter', ylabel='Blutdruck'>
In [11]:
ax.figure
Out[11]:
Dasselbe noch mal für das Konfidenzniveau $1-\alpha=0.99$
In [12]:
ax = sns.regplot(df, x='Alter', y='Blutdruck', ci=99)
vorhersage = res.get_prediction(anfrage).summary_frame(alpha=0.01)
sns.lineplot(x=anfrage.Alter, y=vorhersage.obs_ci_lower, ax = ax, color='orange')
sns.lineplot(x=anfrage.Alter, y=vorhersage.obs_ci_upper, ax=ax, color='orange');
Beispiel: Fische¶
- Fische werden gezüchtet. In den ersten 24 Monaten wurden die folgenden Daten erhoben
- Diesen Daten werden benutzt, um das Wachstum der nächsten Generation zu prognostizieren
In [13]:
df = pd.read_csv('fische.csv')
df.head()
Out[13]:
Monat | Höhe | Gewicht | |
---|---|---|---|
0 | 4 | 18.361636 | 351.447570 |
1 | 18 | 82.891739 | 1489.847354 |
2 | 16 | 75.875655 | 1456.778649 |
3 | 5 | 24.685312 | 466.510582 |
4 | 23 | 102.858653 | 1992.998583 |
- Gewicht in g
- Höhe in mm
- Ein Züchter hat 1200 Fische in seinen Teichen, die alle gleichzeitig geschlüpft sind
- erste Frage: Was ist das Konfidenzintervall für das Gesamtgewicht dieser Fische nach 18 Monaten zum Konfidenzniveau 95%?
- zweite Frage: Wie muss das Netz gewählt werden, um nach 18 Monaten 97.5% der Fische zu fangen?
In [14]:
formel1 = 'Gewicht ~ Monat'
modell1 = smf.ols(formel1, df)
In [15]:
res = modell1.fit()
res.summary()
Out[15]:
Dep. Variable: | Gewicht | R-squared: | 0.992 |
---|---|---|---|
Model: | OLS | Adj. R-squared: | 0.992 |
Method: | Least Squares | F-statistic: | 8866. |
Date: | Tue, 17 Jun 2025 | Prob (F-statistic): | 9.01e-74 |
Time: | 07:58:59 | Log-Likelihood: | -365.66 |
No. Observations: | 70 | AIC: | 735.3 |
Df Residuals: | 68 | BIC: | 739.8 |
Df Model: | 1 | ||
Covariance Type: | nonrobust |
coef | std err | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
Intercept | -2.6497 | 12.964 | -0.204 | 0.839 | -28.519 | 23.219 |
Monat | 83.7359 | 0.889 | 94.161 | 0.000 | 81.961 | 85.510 |
Omnibus: | 3.720 | Durbin-Watson: | 1.931 |
---|---|---|---|
Prob(Omnibus): | 0.156 | Jarque-Bera (JB): | 3.556 |
Skew: | -0.198 | Prob(JB): | 0.169 |
Kurtosis: | 4.030 | Cond. No. | 34.8 |
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
In [16]:
anfrage = pd.DataFrame()
anfrage['Monat'] = [18]
res.get_prediction(anfrage).summary_frame()
Out[16]:
mean | mean_se | mean_ci_lower | mean_ci_upper | obs_ci_lower | obs_ci_upper | |
---|---|---|---|---|---|---|
0 | 1504.597078 | 6.904729 | 1490.818905 | 1518.375251 | 1412.618172 | 1596.575983 |
- untere Vertrauensgrenze für das Gesamtgewicht von 1200 Fischen in kg:
In [17]:
1200 * 1490.8 / 1000
Out[17]:
1788.96
Mit 97.5% Sicherheit werden mindestens 1789 kg Fisch geerntet
In [18]:
formel2 = 'Höhe ~ Monat'
modell2 = smf.ols(formel2, df)
res = modell2.fit()
res.summary()
Out[18]:
Dep. Variable: | Höhe | R-squared: | 0.995 |
---|---|---|---|
Model: | OLS | Adj. R-squared: | 0.995 |
Method: | Least Squares | F-statistic: | 1.474e+04 |
Date: | Tue, 17 Jun 2025 | Prob (F-statistic): | 3.13e-81 |
Time: | 07:58:59 | Log-Likelihood: | -143.68 |
No. Observations: | 70 | AIC: | 291.4 |
Df Residuals: | 68 | BIC: | 295.9 |
Df Model: | 1 | ||
Covariance Type: | nonrobust |
coef | std err | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
Intercept | -0.2723 | 0.544 | -0.501 | 0.618 | -1.358 | 0.813 |
Monat | 4.5292 | 0.037 | 121.402 | 0.000 | 4.455 | 4.604 |
Omnibus: | 3.746 | Durbin-Watson: | 1.917 |
---|---|---|---|
Prob(Omnibus): | 0.154 | Jarque-Bera (JB): | 3.670 |
Skew: | 0.521 | Prob(JB): | 0.160 |
Kurtosis: | 2.583 | Cond. No. | 34.8 |
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
In [19]:
res.get_prediction(anfrage).summary_frame()
Out[19]:
mean | mean_se | mean_ci_lower | mean_ci_upper | obs_ci_lower | obs_ci_upper | |
---|---|---|---|---|---|---|
0 | 81.252727 | 0.289668 | 80.674704 | 81.83075 | 77.394018 | 85.111436 |
Um 97.5% der Fische zu fangen, muss das Netz so beschaffen sein, dass ein Fisch der Höhe 77.4mm nicht hindurch schlüpft
Vorhersagen bei mehreren erklärenden Variablen¶
In [20]:
df = pd.read_csv('galton.csv')
df.describe()
Out[20]:
father | mother | midparentHeight | children | childNum | childHeight | |
---|---|---|---|---|---|---|
count | 481.000000 | 481.000000 | 481.000000 | 481.000000 | 481.000000 | 481.000000 |
mean | 69.137630 | 64.033264 | 69.146778 | 6.008316 | 2.257796 | 69.234096 |
std | 2.305767 | 2.321623 | 1.775891 | 2.669257 | 1.402404 | 2.623905 |
min | 62.000000 | 58.000000 | 64.400000 | 1.000000 | 1.000000 | 60.000000 |
25% | 68.000000 | 63.000000 | 68.020000 | 4.000000 | 1.000000 | 67.500000 |
50% | 69.000000 | 64.000000 | 69.180000 | 6.000000 | 2.000000 | 69.200000 |
75% | 70.500000 | 66.000000 | 70.140000 | 8.000000 | 3.000000 | 71.000000 |
max | 78.500000 | 70.500000 | 75.430000 | 15.000000 | 10.000000 | 79.000000 |
In [21]:
df.gender.value_counts()
Out[21]:
gender male 481 Name: count, dtype: int64
In [22]:
formel = "childHeight ~ father + mother"
model = smf.ols(formel, df)
In [23]:
res = model.fit()
In [24]:
res.summary()
Out[24]:
Dep. Variable: | childHeight | R-squared: | 0.238 |
---|---|---|---|
Model: | OLS | Adj. R-squared: | 0.235 |
Method: | Least Squares | F-statistic: | 74.62 |
Date: | Tue, 17 Jun 2025 | Prob (F-statistic): | 6.25e-29 |
Time: | 07:58:59 | Log-Likelihood: | -1080.7 |
No. Observations: | 481 | AIC: | 2167. |
Df Residuals: | 478 | BIC: | 2180. |
Df Model: | 2 | ||
Covariance Type: | nonrobust |
coef | std err | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
Intercept | 19.3128 | 4.095 | 4.716 | 0.000 | 11.266 | 27.359 |
father | 0.4176 | 0.046 | 9.154 | 0.000 | 0.328 | 0.507 |
mother | 0.3288 | 0.045 | 7.258 | 0.000 | 0.240 | 0.418 |
Omnibus: | 10.653 | Durbin-Watson: | 1.592 |
---|---|---|---|
Prob(Omnibus): | 0.005 | Jarque-Bera (JB): | 14.542 |
Skew: | -0.200 | Prob(JB): | 0.000695 |
Kurtosis: | 3.752 | Cond. No. | 3.69e+03 |
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 3.69e+03. This might indicate that there are
strong multicollinearity or other numerical problems.
- Wie groß ist ein Sohn, dessen Vater 68.9 Zoll und dessen Mutter 66.4 Zoll groß ist?
In [25]:
anfrage = pd.DataFrame()
anfrage['father'] = [68.9]
anfrage['mother'] = [66.4]
anfrage
Out[25]:
father | mother | |
---|---|---|
0 | 68.9 | 66.4 |
In [26]:
res.get_prediction(anfrage).summary_frame()
Out[26]:
mean | mean_se | mean_ci_lower | mean_ci_upper | obs_ci_lower | obs_ci_upper | |
---|---|---|---|---|---|---|
0 | 69.912992 | 0.150896 | 69.616491 | 70.209492 | 65.393024 | 74.43296 |
Für die Größe der Sohns wird ein Wert von 69.91 Zoll erwartet
midparentHeight¶
- Zu Galtons Zeiten (1822 - 1911) gab es weder die Theorie der Linearen Modelle noch die dazu benötigte Rechnerausstattung
- Daher musste er die Daten von Vater und Mutter zu einer fiktiven Größe zusammenfassen
- Diese nannte er midparentHeight
- Im Internet steht dazu viel unverständliches
- statistics to the rescue
In [27]:
formel_mph = "midparentHeight ~ father + mother"
modell_mph = smf.ols(formel_mph, df)
res_mph = modell_mph.fit()
In [28]:
res_mph.summary()
Out[28]:
Dep. Variable: | midparentHeight | R-squared: | 1.000 |
---|---|---|---|
Model: | OLS | Adj. R-squared: | 1.000 |
Method: | Least Squares | F-statistic: | 6.087e+26 |
Date: | Tue, 17 Jun 2025 | Prob (F-statistic): | 0.00 |
Time: | 07:58:59 | Log-Likelihood: | 12557. |
No. Observations: | 481 | AIC: | -2.511e+04 |
Df Residuals: | 478 | BIC: | -2.510e+04 |
Df Model: | 2 | ||
Covariance Type: | nonrobust |
coef | std err | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
Intercept | 4.518e-13 | 1.99e-12 | 0.227 | 0.820 | -3.46e-12 | 4.36e-12 |
father | 0.5000 | 2.22e-14 | 2.26e+13 | 0.000 | 0.500 | 0.500 |
mother | 0.5400 | 2.2e-14 | 2.45e+13 | 0.000 | 0.540 | 0.540 |
Omnibus: | 14.588 | Durbin-Watson: | 0.000 |
---|---|---|---|
Prob(Omnibus): | 0.001 | Jarque-Bera (JB): | 18.675 |
Skew: | 0.299 | Prob(JB): | 8.80e-05 |
Kurtosis: | 3.757 | Cond. No. | 3.69e+03 |
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 3.69e+03. This might indicate that there are
strong multicollinearity or other numerical problems.
Also
midparentHeight = (father + 1.08 * mother)/2
ohne jeden zufälligen Anteil