Streuungsmaße

import random

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy.stats import norm, gaussian_kde

Die Maße der zentralen Tendenz, wie Mittelwert, Median und Modus, geben nicht das ganze Bild der Verteilung eines Datensatzes wieder. Zwei Datensätze mit demselben Mittelwert können völlig unterschiedliche Streuungen aufweisen. Die Streuung der Beobachtungswerte des einen Datensatzes kann viel größer oder kleiner sein als die des anderen Datensatzes. Daher ist der Mittelwert, Median oder Modus allein in der Regel kein ausreichendes Maß, um die Form der Verteilung eines Datensatzes aufzuzeigen. Wir benötigen auch ein Maß, das Informationen über die Variation zwischen den Datenwerten liefert. Diese Maße werden als Streuungsmaße bezeichnet. Die Maße der zentralen Tendenz und der Streuung ergeben zusammengenommen ein besseres Bild eines Datensatzes als die Maße der zentralen Tendenz allein ([Fahrmeir et al., 2016] s.65).

Varianz und Standardabweichung

Die Varianz ist die Summe der quadrierten Abweichungen vom Mittelwert. Die Varianz für Populationsdaten wird mit \(\sigma^2\) bezeichnet (gelesen als Sigma-Quadrat), und die für Stichprobendaten berechnete Varianz wird mit \(s^2\) bezeichnet.

\[ \sigma^2 = \frac{\sum_{i=1}^n (x_i - \mu)^2}{N} \]

und $\( s^2 = \frac{\sum_{i=1}^n (x_i - \bar{x})^2}{n-1} \)$

wobei \(\sigma^2\) die Varianz der Grundgesamtheit und \(s^2\) die Stichprobenvarianz ist. Die Größe \(x_i-\mu\) oder \(x_i-\bar{x}\) in den obigen Formeln wird als die Abweichung des \(x_i\)-Wertes \((x_1,x_2, \dots ,x_n)\) vom Mittelwert bezeichnet ([Fahrmeir et al., 2016] s.64).

Die Standardabweichung ist das gebräuchlichste Maß für die Streuung. Der Wert der Standardabweichung gibt an, wie eng die Werte eines Datensatzes um den Mittelwert herum gestreut sind. Im Allgemeinen zeigt ein niedriger Wert der Standardabweichung für einen Datensatz an, dass die Werte dieses Datensatzes über einen relativ kleineren Bereich um den Mittelwert herum verteilt sind. Im Gegensatz dazu zeigt ein größerer Wert der Standardabweichung für einen Datensatz an, dass die Werte dieses Datensatzes über einen relativ größeren Bereich um den Mittelwert herum gestreut sind ([Fahrmeir et al., 2016] s.65).

import matplotlib.pyplot as plt

TITLESIZE = 17
SEED = np.random.seed(42)
SIZE = 100
x = np.linspace(0, 1, SIZE)

fig, ax = plt.subplots(ncols=2, figsize=(16, 6))

y = np.random.normal(loc=0.5, scale=1.0, size=SIZE)
ax[0].plot(x, y, "o")
ax[0].set_title("Geringe Abweichungen um den Mittelwert", size=TITLESIZE)

y = np.random.normal(loc=0.5, scale=3.0, size=SIZE)
ax[1].plot(x, y, "o")
ax[1].set_title("Grosse Abweichungen um den Mittelwert", size=TITLESIZE)

for _ax in ax:
    _ax.set_xticks([])
    _ax.set_yticks([])
    _ax.axhline(0.5, color="k")
    _ax.set_ylim(-6, 6)
    _ax.legend(["Daten", "Mittelwert"])
../_images/03_Streuungsmaße_6_0.png

Die Standardabweichung erhält man durch Ziehen der Quadratwurzel aus der Varianz. Folglich wird die für Grundgesamtheitsdaten berechnete Standardabweichung mit \(\sigma\) und die für Stichprobendaten berechnete Standardabweichung mit \(s\) bezeichnet.

\[ \sigma = \sqrt{\frac{\sum_{i=1}^n (x_i - \mu)^2}{N}} \]

und

\[ s = \sqrt{\frac{\sum_{i=1}^n (x_i - \bar{x})^2}{n-1}} \]

wobei \(\sigma\) die Standardabweichung der Grundgesamtheit und \(s\) die Standardabweichung der Stichprobe ist.

Als Übung berechnen wir für einige numerische Variablen, die im students Datensatz von Interesse sind, den Mittelwert, den Median, die Varianz und die Standardabweichung und stellen sie in einem Dataframe dar.

# Lesen der Datei students.csv als Dataframe; nur die Spalten "age", "weight", "height" und "nc.score" Spalten werden eingelesen

students = pd.read_csv(
    "../../data/students.csv",
    index_col=0,
    usecols=["age", "height", "weight", "nc.score"],
)
# Zeige die ersten 10 Werte
students.head(10)
age height weight nc.score
1 19 160 64.8 1.91
2 19 172 73.0 1.56
3 22 168 70.6 1.24
4 19 183 79.7 1.37
5 21 175 71.4 1.46
6 19 189 85.8 1.34
7 21 156 65.9 1.11
8 21 167 65.7 2.03
9 18 195 94.4 1.29
10 18 165 66.0 1.19

Pandas verfügt über die Methode agg. Diese lässt uns sehr einfach verschiedene deskritptive Statisitken berechnen.

students.agg(["mean", "median", "var", "std"])
age height weight nc.score
mean 22.541571 171.380750 72.998131 2.166481
median 21.000000 171.000000 71.800000 2.040000
var 36.785568 122.711652 74.566017 0.658610
std 6.065111 11.077529 8.635162 0.811548

Verwendung der Standardabweichung

Mit Hilfe des Mittelwerts und der Standardabweichung lässt sich der Anteil oder Prozentsatz der Gesamtbeobachtungen ermitteln, die in ein bestimmtes Intervall um den Mittelwert fallen.

Tschebyscheff-Theorem

Das Tschebyscheff Ungleichung gibt eine untere Schranke für die Fläche unter einer Kurve zwischen zwei Punkten, die auf gegenüberliegenden Seiten des Mittelwerts und im gleichen Abstand vom Mittelwert liegen.

Für jede Zahl \(k\), die größer als \(1\) ist, liegen mindestens \(1-\frac{1}{k^2}\) der Datenwerte innerhalb von \(k\) Standardabweichungen vom Mittelwert.

Lassen Sie uns Python verwenden, um ein Gefühl für den Tschebyscheff-Theorem zu bekommen.

k = np.arange(1, 4.1, 0.1)
value = np.round((1 - (1 / k**2)) * 100)
chebyshev = pd.DataFrame({"k": k, "Prozent": value})
chebyshev
k Prozent
0 1.0 0.0
1 1.1 17.0
2 1.2 31.0
3 1.3 41.0
4 1.4 49.0
5 1.5 56.0
6 1.6 61.0
7 1.7 65.0
8 1.8 69.0
9 1.9 72.0
10 2.0 75.0
11 2.1 77.0
12 2.2 79.0
13 2.3 81.0
14 2.4 83.0
15 2.5 84.0
16 2.6 85.0
17 2.7 86.0
18 2.8 87.0
19 2.9 88.0
20 3.0 89.0
21 3.1 90.0
22 3.2 90.0
23 3.3 91.0
24 3.4 91.0
25 3.5 92.0
26 3.6 92.0
27 3.7 93.0
28 3.8 93.0
29 3.9 93.0
30 4.0 94.0

Um es in Worte zu fassen: Für \(k=2\) bedeutet das, dass mindestens \(75 \% \) der Datenwerte innerhalb von 2 Standardabweichungen vom Mittelwert liegen.

Stellen wir das Tschebyscheff-Theorem mit Python dar:

fig, ax = plt.subplots()
chebyshev.plot.scatter(x="k", y="Prozent", ax=ax)
ax.set_title("Tschebyscheff-Theorem")
ax.grid()
../_images/03_Streuungsmaße_17_0.png

Das Theorem gilt sowohl für Stichproben- als auch für Grundgesamtheitsdaten. Das Tschebyscheffsche Ungleichung gilt für Verteilungen beliebiger Form. Es kann jedoch nur für \(k > 1\) verwendet werden. Denn wenn \(k=1\) ist, ist der Wert von \(1-\frac{1}{k^2}\) Null, und wenn \(k < 1\) ist, ist der Wert von \(1-\frac{1}{k^2}\) negativ ([Fahrmeir et al., 2016] s.304).

Empirische Regel

Während das Tschebyscheffsche Ungleichung auf jede Art von Verteilung anwendbar ist, gilt die empirische Regel nur für eine bestimmte Art von Verteilung, die so genannte Gaußverteilung oder Normalverteilung. Es gibt 3 Regeln:

Bei einer Normalverteilung sind

  1. \(68 \%\) der Beobachtungen innerhalb einer Standardabweichung des Mittelwerts.

  2. \(95 \%\) der Beobachtungen innerhalb von zwei Standardabweichungen des Mittelwerts.

  3. \(99,7 \%\) der Beobachtungen innerhalb von drei Standardabweichungen des Mittelwerts.

Da wir inzwischen über genügend Hacking-Power verfügen, werden wir versuchen zu testen, ob die drei Regeln gültig sind.

(1) Erstens werden wir die Funktion random.normal() in Python erforschen, um normalverteilte Daten zu erzeugen, und

(2) Zweitens werden wir zu unserem students Datensatz zurückkehren und diese Regeln an diesem Datensatz zu validieren.

Die Normalverteilung gehört zur Familie der stetigen Verteilungen. In Python gibt es eine Vielzahl von Wahrscheinlichkeitsverteilungen (hier). Um Daten aus einer Normalverteilung zu erzeugen, kann man die Funktion random.normal() verwenden, die ein Zufallsvariablengenerator für die Normalverteilung ist.

Mit der Funktion np.random.normal(loc=0.0, scale=1.0) können wir Zufallsvariablen aus einer Normalverteilung mit einem gegebenen Mittelwert (Standard ist \(0\)) und einer Standardabweichung (Standard ist \(1\)) entnehmen. Mit dem Argument size können wir die Anzahl der erzeugten Zufallsvariablen bestimmen.

np.random.normal(loc=0.0, scale=1.0, size=1)
array([0.35778736])
np.random.normal(loc=0.0, scale=1.0, size=1)
array([0.56078453])
np.random.normal(loc=0.0, scale=1.0, size=1)
array([1.08305124])

Wir können die Funktion ziemlich einfach bitten, Hunderte oder Tausende oder noch mehr (Pseudo-)Zufallszahlen zu ziehen:

np.random.normal(loc=0.0, scale=1.0, size=10)
array([ 1.05380205, -1.37766937, -0.93782504,  0.51503527,  0.51378595,
        0.51504769,  3.85273149,  0.57089051,  1.13556564,  0.95400176])
np.random.normal(loc=0.0, scale=1.0, size=100)
array([ 0.65139125, -0.31526924,  0.75896922, -0.77282521, -0.23681861,
       -0.48536355,  0.08187414,  2.31465857, -1.86726519,  0.68626019,
       -1.61271587, -0.47193187,  1.0889506 ,  0.06428002, -1.07774478,
       -0.71530371,  0.67959775, -0.73036663,  0.21645859,  0.04557184,
       -0.65160035,  2.14394409,  0.63391902, -2.02514259,  0.18645431,
       -0.66178646,  0.85243333, -0.79252074, -0.11473644,  0.50498728,
        0.86575519, -1.20029641, -0.33450124, -0.47494531, -0.65332923,
        1.76545424,  0.40498171, -1.26088395,  0.91786195,  2.1221562 ,
        1.03246526, -1.51936997, -0.48423407,  1.26691115, -0.70766947,
        0.44381943,  0.77463405, -0.92693047, -0.05952536, -3.24126734,
       -1.02438764, -0.25256815, -1.24778318,  1.6324113 , -1.43014138,
       -0.44004449,  0.13074058,  1.44127329, -1.43586215,  1.16316375,
        0.01023306, -0.98150865,  0.46210347,  0.1990597 , -0.60021688,
        0.06980208, -0.3853136 ,  0.11351735,  0.66213067,  1.58601682,
       -1.2378155 ,  2.13303337, -1.9520878 , -0.1517851 ,  0.58831721,
        0.28099187, -0.62269952, -0.20812225, -0.49300093, -0.58936476,
        0.8496021 ,  0.35701549, -0.6929096 ,  0.89959988,  0.30729952,
        0.81286212,  0.62962884, -0.82899501, -0.56018104,  0.74729361,
        0.61037027, -0.02090159,  0.11732738,  1.2776649 , -0.59157139,
        0.54709738, -0.20219265, -0.2176812 ,  1.09877685,  0.82541635])

Wenn wir ein Histogramm dieser Zahlen erstellen, sehen wir die namensgebende glockenförmige Verteilung.

y_norm = np.random.normal(loc=0.0, scale=1.0, size=100000)
bins = int(len(y_norm) / 1000)  # bestimmt die Anzahl der Bins
plt.hist(y_norm, bins=bins)
plt.ylabel("Absolute Häufigkeit")
plt.show()
../_images/03_Streuungsmaße_28_0.png

Wir kennen bereits den Mittelwert und die Standardabweichung der Werte in y_norm, da wir die Funktion np.random.normal() explizit mit mean=0 und sd=1 aufgerufen haben. Wir müssen also nur die Zahlen in y_norm zählen, die größer als \(-1\) bzw. kleiner als \(1\) und \(2\) bzw. \(-2\) und \(3\) bzw. \(-3\) sind, und sie zur Länge von y_norm, in unserem Fall \(100.000\), in Beziehung setzen, um die drei oben genannten Regeln zu bestätigen.

# Berechne Anzahl der Werte < 1 - Anzahl der Werte > -1 durch Gesamtanzahl
sd1 = sum((y_norm > -1) & (y_norm < 1)) / len(y_norm)

# Berechne Anzahl der Werte < 2 - Anzahl der Werte  > -2 durch Gesamtanzahl
sd2 = sum((y_norm > -2) & (y_norm < 2)) / len(y_norm)

# Berechne Anzahl der Werte < 3 - Anzahl der Werte > -3 durch Gesamtanzahl
sd3 = sum((y_norm > -3) & (y_norm < 3)) / len(y_norm)

print("sd1 :", sd1)
print("sd2 :", sd2)
print("sd3 :", sd3)
sd1 : 0.68143
sd2 : 0.95444
sd3 : 0.99737

Perfekte Übereinstimmung! Die drei empirischen Regeln sind offensichtlich gültig. Um unsere Ergebnisse zu veranschaulichen, stellen wir das Histogramm erneut dar und fügen einige Anmerkungen hinzu. Bitte beachten Sie, dass wir in der hist()-Funktion das Argument density=True setzen. Dies hat zur Folge, dass das resultierende Histogramm nicht mehr die Zählungen auf der y-Achse anzeigt, sondern die Dichtewerte (normalisierte Zählung geteilt durch Bin-Breite), was bedeutet, dass sich die Balkenbereiche zu \(1\) summieren.

fig, ax = plt.subplots()
ax.hist(y_norm, bins=bins)
ax.set_ylabel("Absolute Häufigkeit")
for e, std in enumerate([(-1, 1), (-2, 2), (-3, 3)]):
    plt.axvline(std[0], color=f"C{e+1}")
    plt.axvline(std[1], color=f"C{e+1}")
../_images/03_Streuungsmaße_32_0.png

Nun, lassen Sie uns an der zweiten Aufgabe arbeiten: Überprüfen Sie die drei empirischen Regeln anhand des students Datensatzes. Dazu müssen wir überprüfen, ob eine der numerischen Variablen im Studentendatensatz normalverteilt ist. Wir beginnen mit der Extraktion numerischer Variablen von Interesse aus dem students Datensatz. Dann zeichnen wir Histogramme und beurteilen, ob die Variable normalverteilt ist oder nicht. Zunächst überprüfen wir jedoch den Datensatz, indem wir die Funktion head() aufrufen.

# Lesen der Datei students.csv als Dataframe;
students = pd.read_csv(
    "../../data/students.csv",
    usecols=["age", "height", "weight", "score1", "score2", "salary"],
)
students.head(10)
age height weight score1 score2 salary
1 19 160 64.8 NaN NaN NaN
2 19 172 73.0 NaN NaN NaN
3 22 168 70.6 45.0 46.0 NaN
4 19 183 79.7 NaN NaN NaN
5 21 175 71.4 NaN NaN NaN
6 19 189 85.8 NaN NaN NaN
7 21 156 65.9 NaN NaN NaN
8 21 167 65.7 58.0 62.0 NaN
9 18 195 94.4 57.0 67.0 NaN
10 18 165 66.0 NaN NaN NaN

Um einen Überblick über die Form der Verteilung der einzelnen Variablen zu erhalten, verwenden wir die Methode hist().

cols = students.columns
fig, ax = plt.subplots(ncols=int(len(cols) / 2), nrows=2)
ax = np.ravel(ax)  # vereinfacht das iterieren durch die axes Objekte
titles = {
    "age": "Alter",
    "height": "Grösse",
    "weight": "Gewicht",
    "score1": "Punkte1",
    "score2": "Punkte2",
    "salary": "Gehalt",
}

for e, col in enumerate(cols):
    bins = int(students[col].nunique() / 2)
    ax[e].hist(students[col], bins=bins, density=True)
    ax[e].set_title(titles[col])
fig.tight_layout()
../_images/03_Streuungsmaße_36_0.png

Wir stellen sofort fest, dass einige Variablen positiv verzerrt sind, also schließen wir sie aus und behalten diejenigen, die normal verteilt zu sein scheinen.

cols = ["height", "salary"]
fig, ax = plt.subplots(ncols=2)
ax = np.ravel(ax)  # vereinfacht das iterieren durch die axes Objekte
bins_fact = {"height": 2, "salary": 40}

for e, col in enumerate(cols):
    bins = int(students[col].nunique() / bins_fact[col])
    ax[e].hist(students[col], bins=bins)
    ax[e].set_title(titles[col])
fig.tight_layout()
../_images/03_Streuungsmaße_38_0.png

Nun, sowohl die Variable height als auch die Variable salary scheinen mehr oder weniger normalverteilt zu sein. Es ist also eine Frage des Geschmacks, welche Variable man für die weitere Analyse auswählt. Für den Moment bleiben wir bei der Gehaltsvariable und überprüfen, ob die drei oben genannten empirischen Regeln gültig sind. Wir wechseln zu Python und validieren diese Regeln, indem wir zunächst den Mittelwert und die Standardabweichungen berechnen. Bitte beachten Sie, dass die Gehaltsvariable Fehlwerte enthält, die mit NA gekennzeichnet sind. Daher schließen wir zunächst alle NA-Werte aus, indem wir die Funktion dropna() anwenden.

salary = students["salary"].dropna()
salary
11      45254.108021
13      40552.790243
14      27007.030294
17      33969.159268
28      50617.641870
            ...     
8229    33259.703079
8234    41028.241341
8235    36750.087135
8238    40112.041834
8239    45900.134459
Name: salary, Length: 1753, dtype: float64
print(f"Mittelwert des Gehalts:             {salary.mean()}")
print(f"1 Standardabweichung des Gehalts:   {salary.std()}")
print(f"2 Standardabweichungen des Gehalts: {2 * salary.std()}")
print(f"3 Standardabweichungen des Gehalts: {3 * salary.std()}")
Mittelwert des Gehalts:             42522.112364224806
1 Standardabweichung des Gehalts:   10333.139905546619
2 Standardabweichungen des Gehalts: 20666.279811093238
3 Standardabweichungen des Gehalts: 30999.41971663986

Wie in der obigen allgemeinen Beispielform zählen wir die Anzahl der Werte, die größer als \(+1\) s.d. bzw. kleiner als \(-1\) s.d. und \(+2\) s.d. bzw. \(-2\) s.d. und \(+3\) s.d. bzw. \(-3\) s.d. sind, und setzen sie in Beziehung zur Länge des Vektors, in unserem Fall \(1753\).

salary_mean = salary.mean()
salary_std = salary.std()

sd1 = (
    sum((salary > (salary_mean - salary_std)) & (salary < (salary_mean + salary_std)))
    / salary.shape[0]
)

sd2 = (
    sum(
        (salary > (salary_mean - 2 * salary_std))
        & (salary < (salary_mean + 2 * salary_std))
    )
    / salary.shape[0]
)


sd3 = (
    sum(
        (salary > (salary_mean - 3 * salary_std))
        & (salary < (salary_mean + 3 * salary_std))
    )
    / salary.shape[0]
)


print("sd1 :", sd1)
print("sd2 :", sd2)
print("sd3 :", sd3)
sd1 : 0.6708499714774672
sd2 : 0.9560752994865944
sd3 : 0.9977181973759269

Wow, ziemlich nah dran! Offensichtlich zeigt die Gehaltsvariable eine starke Tendenz zur Unterstützung der so genannten empirischen Regel. Wir stellen das Histogramm für die Variable salary dar, um unseren Eindruck zu bestätigen.

fig, ax = plt.subplots()
bins = int(salary.nunique() / 40)
ax.hist(salary, bins=bins)
ax.set_ylabel("Absolute Häufigkeit")
ax.set_xlabel("Gehalt")
for e in range(3):
    ax.axvline(salary_mean + (e + 1) * salary_std, color=f"C{e+1}")
    ax.axvline(salary_mean - (e + 1) * salary_std, color=f"C{e+1}")
../_images/03_Streuungsmaße_45_0.png

Wir können nun unseren Visualisierungsansatz erweitern, indem wir die empirische Dichteschätzung mit der Funktion scipy_kernel.evaluate() grafisch darstellen und ihre Form überprüfen. Wir stellen die empirische Dichteschätzung als gestrichelte Linie dar, indem wir das (Linientyp-Argument) '-.' und eine Linienbreite von \(3\) (Argument linewidth=3.0) setzen.

fig, ax = plt.subplots()
bins = int(salary.nunique() / 40)
ax.hist(salary, bins=bins, density=True)
ax.set_ylabel("Dichte")
ax.set_xlabel("Gehalt")

# empirische Dichteschätzung
x_salary = np.linspace(salary.min(), salary.max(), 100)
scipy_kernel = gaussian_kde(salary)
dens_emp = scipy_kernel.evaluate(x_salary)
ax.plot(x_salary, dens_emp, color="k", linestyle="dashed", linewidth=3.0)

# Standardabweichungen
for e in range(3):
    ax.axvline(salary_mean + (e + 1) * salary_std, color=f"C{e+1}")
    ax.axvline(salary_mean - (e + 1) * salary_std, color=f"C{e+1}")
../_images/03_Streuungsmaße_47_0.png

Schließlich vergleichen wir unsere empirische Dichteschätzung mit der theoretischen Wahrscheinlichkeitsdichtefunktion, die auf dem tatsächlichen Mittelwert und der Standardabweichung der Daten salary basiert. Für einen besseren visuellen Vergleich wechseln wir zurück zu einer nicht eingefärbten Histogramm-Darstellung.

fig, ax = plt.subplots()
bins = int(salary.nunique() / 40)
ax.hist(salary, bins=bins, density=True)
ax.set_ylabel("Dichte")
ax.set_xlabel("Gehalt")

# empirische Dichteschätzung
x_salary = np.linspace(salary.min(), salary.max(), 100)
scipy_kernel = gaussian_kde(salary)
dens_emp = scipy_kernel.evaluate(x_salary)
ax.plot(
    x_salary,
    dens_emp,
    color="k",
    linestyle="dashed",
    linewidth=3.0,
    label="Empirische Dichteschätzung",
)

# Wahrscheinlichkeitsdichtefunktion
pdf = norm.pdf(x_salary, loc=salary.mean(), scale=salary.std())
ax.plot(
    x_salary, pdf, color="k", linewidth=3.0, label="Wahrscheinlichkeitsdichtefunktion"
)

# Standardabweichungen
for e in range(3):
    ax.axvline(salary_mean + (e + 1) * salary_std, color=f"C{e+1}")
    ax.axvline(salary_mean - (e + 1) * salary_std, color=f"C{e+1}")
ax.legend()
<matplotlib.legend.Legend at 0x170432380>
../_images/03_Streuungsmaße_49_1.png

Wir können daraus schließen, dass salary im Datensatz der students ungefähr normalverteilt ist. Die Grafik zeigt jedoch, dass die Verteilung der Gehaltsvariablen leicht nach linksschief ist. Dies ist an der Abweichung zwischen der empirischen Dichteschätzung und der Wahrscheinlichkeitsdichtefunktion zu erkennen.

Die Spannweite

Die Spannweite als Maß für die Streuung ist einfach zu berechnen. Sie ergibt sich aus der Differenz zwischen dem größten und dem kleinsten Wert in einem Datensatz.

\[\text{Range} = \text{kleinster Wert} - \text{größter Wert}\]

Betrachten wir unseren students Datensatz. Wir unterteilen den Datensatz so, dass er nur numerische Daten enthält.

df = pd.read_csv("../../data/students.csv")
df.sample(10)
stud.id name gender age height weight religion nc.score semester major minor score1 score2 online.tutorial graduated salary
3600 255883 Morrison-Cook, Austin Male 20 172 72.6 Other 1.67 4th Mathematics and Statistics Economics and Finance 91.0 87.0 0 0 NaN
2082 686345 Lenahan, Kyle Male 21 181 78.0 Other 1.38 2nd Social Sciences Economics and Finance NaN NaN 0 0 NaN
522 346731 Lee, Nicholas Male 22 192 93.1 Catholic 1.27 5th Environmental Sciences Social Sciences 78.0 65.0 1 1 49068.502177
5604 963305 Kramer, David Male 56 170 72.6 Orthodox 2.14 5th Environmental Sciences Mathematics and Statistics 72.0 71.0 1 1 42606.347228
3671 124687 Than, Jonathan Male 22 172 72.4 Protestant 1.79 4th Environmental Sciences Mathematics and Statistics 62.0 69.0 0 1 49004.727119
5376 696262 Macasinag, Paulo Male 20 197 94.3 Catholic 3.41 6th Political Science Environmental Sciences 51.0 47.0 1 1 39442.803935
2318 452190 Briseno, Alisha Female 63 162 68.0 Catholic 2.19 2nd Mathematics and Statistics Environmental Sciences NaN NaN 0 0 NaN
4840 955228 Thapa, Richard Male 23 175 76.0 Other 2.48 1st Mathematics and Statistics Environmental Sciences NaN NaN 0 0 NaN
1286 854649 Duling, Joshua Ian Male 61 170 70.3 Protestant 1.54 >6th Economics and Finance Mathematics and Statistics 64.0 69.0 1 0 NaN
118 582922 Valenciano-Poffenbarger, Quincie Female 19 175 76.8 Catholic 2.19 1st Social Sciences Mathematics and Statistics NaN NaN 0 0 NaN

Wir sind also an den Kategorien age, height, weight und nc.score interessiert. Wir verwenden die Methoden min() und max(), um das Minimum und Maximum der ausgewählten Variablen zu berechnen. Erneut greifen wir auf die agg-Methode zurück.

summary = df[["age", "height", "weight", "nc.score"]].agg(["min", "max"])
summary
age height weight nc.score
min 18 135 51.4 1.0
max 64 206 116.0 4.0

Um nun die Spannweite für jede Variable zu berechnen, müssen wir nur eine Zeile von der anderen abziehen.

summary.loc["Spannweite"] = summary.loc["max"] - summary.loc["min"]
summary
age height weight nc.score
min 18.0 135.0 51.4 1.0
max 64.0 206.0 116.0 4.0
Spannweite 46.0 71.0 64.6 3.0

Die Spannweite hat wie der Mittelwert den Nachteil, dass sie durch Ausreißer beeinflusst wird. Daher ist die Spannweite kein gutes Streuungsmaß für einen Datensatz, der Ausreißer enthält. Ein weiterer Nachteil der Verwendung der Spannweite als Streuungsmaß ist, dass ihre Berechnung nur auf zwei Werten basiert: dem größten und dem kleinsten. Alle anderen Werte in einem Datensatz werden bei der Berechnung der Spanne ignoriert. Daher ist die Spannweite oftmals kein sehr zufriedenstellendes Maß für die Streuung ([Fahrmeir et al., 2016] s.62).