Kruskal-Wallis-Test in R¶


Pakete importieren¶

In [17]:
import pandas as pd          # Daten laden und bearbeiten
import numpy as np           # Mathematische Funktionen & Arrays
import matplotlib.pyplot as plt  # Diagramme (z. B. Boxplot)
import seaborn as sns        # Erweiterte Diagramme (z. B. Violinplot)
from scipy.stats import kruskal, norm  # Statistik: Kruskal-Wallis, Z-Werte
import scikit_posthocs as sp # Post-hoc-Tests (z. B. Dunn-Test)
from IPython.display import display  # Tabellen hübsch anzeigen im Notebook

Daten einlesen¶

In [18]:
wallis_df = pd.read_excel("wallis.xlsx")  
wallis_df.head(5)
Out[18]:
ID Altersgruppe Sportlichkeit
0 1 0-30 19.0
1 2 0-30 9.0
2 3 0-30 13.0
3 4 0-30 14.0
4 5 0-30 12.5

Hypothese¶

H0: Es gibt keinen Unterschied zwischen Altersgruppe(0-30/31-55/56+) und der Sportlichkeit gemessen auf dem Laufband in Minuten.

H1: Es gibt einen Unterschied zwischen Altersgruppe(0-30/31-55/56+) und der Sportlichkeit gemessen auf dem Laufband in Minuten.

Voraussetzungen für den Kruskal-Wallis-Test¶

Die abhängige Variable ist mindestens ordinalskaliert -> ist gegeben

Es liegt eine unabhängige Variable vor, mittels der die zu vergleichenden Gruppen gebildet werden. -> Die Altergruppen sind unabhängig.

Deskriptive Statistiken¶

Diese "library(dplyr)" muss verwendet werden.

In [19]:
# Gruppieren nach Altersgruppe, Anzahl und Median berechnen, dann runden
gruppen_stats = (
    wallis_df
    .groupby("Altersgruppe")
    .agg(Anzahl=("Sportlichkeit", "count"),
         Median=("Sportlichkeit", "median"))
    .round(2)
    .reset_index()
)

# Anzeige der Tabelle

display(gruppen_stats)
Altersgruppe Anzahl Median
0 0-30 10 13.5
1 31-55 8 9.2
2 56+ 11 3.0

Die Mediane der Gruppen unterscheiden sich. Die Gruppe "0-30" scheint am sportlichsten zu sein mit einem Wert von 13.5 min (N=10). Die Gruppe "31-55" schaffen 9.2 Min.(N=8) auf dem Laufbahn und am wenigsten sportlich ist die Gruppe der "56+" mit einem Median von 3 (N=11).

Boxplots¶

In [20]:
# Boxplot nach Altersgruppe (entspricht R boxplot(... ~ ...))
plt.figure(figsize=(8, 6))
wallis_df.boxplot(column="Sportlichkeit", by="Altersgruppe")
plt.title("Boxplot: Sportlichkeit nach Altersgruppe")
plt.suptitle("")  # Entfernt den automatischen Titel von pandas
plt.xlabel("Altersgruppe")
plt.ylabel("Sportlichkeit (Minuten)")
plt.grid(True)
plt.show()
<Figure size 800x600 with 0 Axes>
No description has been provided for this image

Die Mediane der Gruppen unterscheiden sich. Es gibt augenscheinlich keine Ausreisser.

ALTERNATIV¶

In [21]:
# Violinplot + Boxplot kombiniert
plt.figure(figsize=(8, 6))
sns.violinplot(data=wallis_df, x="Altersgruppe", y="Sportlichkeit", inner=None, linewidth=0.8)
sns.boxplot(data=wallis_df, x="Altersgruppe", y="Sportlichkeit", width=0.2, showcaps=True, boxprops={'zorder': 2})
plt.title("Boxplot und Violinplot: Sportlichkeit nach Altersgruppe")
plt.xlabel("Altersgruppe")
plt.ylabel("Sportlichkeit (Minuten)")
plt.grid(True)
plt.show()
No description has been provided for this image

Ergebnisse der Kruskal-Wallis-Test¶

In [22]:
# Beispiel: Kruskal-Wallis-Test durchführen
gruppen = [gruppe["Sportlichkeit"].dropna().values 
           for _, gruppe in wallis_df.groupby("Altersgruppe")]
stat, p_value = kruskal(*gruppen)

# 📊 Ergebnistabelle erstellen
ergebnis_df = pd.DataFrame([{
    "Test": "Kruskal-Wallis",
    "Teststatistik (H)": f"{stat:.4f}",
    "p-Wert": f"{p_value:.5f}",
}])
print("Ergebnisse des Kruskal-Wallis-Tests:")
print(ergebnis_df.to_string(index=False))
Ergebnisse des Kruskal-Wallis-Tests:
          Test Teststatistik (H)  p-Wert
Kruskal-Wallis           21.1225 0.00003

Die Tabelle zeigt Unterschiede. Für das Beispiel wird eine Signifikanz von .000 ausgegeben. Also kann davon ausgegangen werden, dass es Unterschiede bezüglich der zentralen Tendenzen der Gruppen gibt (Chi-Quadrat(2) = 21.22,p = .000). Allerdings lässt sich aufgrund dieses Tests nicht bestimmen, welche der drei Gruppen sich signifikant voneinander unterscheiden. Es ist denkbar, dass sich lediglich ein Paar signifikant unterscheidet und zwischen den übrigen keine signifikanten Unterschiede vorliegen. Daher wird ein Post-hoc-Test durchgeführt.

In [ ]:
#from IPython.display import display
# Dunn-Test mit Bonferroni-Korrektur
dunn_df = sp.posthoc_dunn(wallis_df, val_col='Sportlichkeit', group_col='Altersgruppe', p_adjust='bonferroni')

# Ergebnisse anzeigen

display(dunn_df.round(4))
0-30 31-55 56+
0-30 1.0000 0.1227 0.0000
31-55 0.1227 1.0000 0.0771
56+ 0.0000 0.0771 1.0000

Der Post-Hoc-Test ergibt, dass sich alle vergleichenden Gruppen signifikant unterscheiden (p < 0.05).

Es gibt einem signifikanten Unterschied zwischen den drei getesteten Gruppen, das heisst, dass jede Altersgruppe einen deutlichen Unterschied in der Sportlichkeit aufweist.

Es können drei unabhängige Gruppen gebildet werden. Eine Generalisierung ist möglich.

Berechnung der Effektstärke¶

$$r = \Biggl| \frac{z}{\sqrt{n}}\Biggl|$$

mit:

  • $z =$ z-transformiertes $p$ des Chi-Quadrat-Tests nach Kruskal-Wallis.

  • $n =$ Größe der Gesamtstichprobe

In [24]:
# Z-Wert aus p-Wert berechnen (ungerichtet/zweiseitig)
Zstat = norm.ppf(p_value / 2)  # analog zu qnorm in R
Zstat = abs(Zstat)  # Betrag, da Effektstärke positiv

# Stichprobengröße
daten = len(wallis_df)

# Effektstärke berechnen
eff = Zstat / np.sqrt(daten)

# Ergebnisse anzeigen
print(f"Z-Wert für den H-Test: {Zstat:.2f}")
print(f"Anzahl der Daten: {daten}")
print(f"Effektstärke (r): {eff:.2f}")
# 
Z-Wert für den H-Test: 4.21
Anzahl der Daten: 29
Effektstärke (r): 0.78

Zur Beurteilung der Groesse des Effektes dient die Einteilung von Cohen (1992):

$$ \begin{align} \text{Schwacher Effekt: } 0.10 &< ||r|| < 0.30 \\ \text{Schwacher bis mittlerer Effekt: } 0.30 &= ||r|| \\ \text{Mittlerer Effekt: } 0.30 &< ||r|| < 0.50 \\ \text{Mittlerer bis starker Effekt: }0.50 &= ||r|| \\ \text{Starker Effekt: } 0.50 &< ||r|| \end{align} $$

Damit entspricht eine Effektstärke von 0.78 einem starken Effekt.

ALTERNATIVE¶

$$w = \left|\sqrt\frac{\mathcal{X}^2}{n}\right|$$ mit:

  • ${X}^2 =$ Chi-Quadrat-Wert nach Kruskal-Wallis.

  • $n =$ Größe der Gesamtstichprobe

In [25]:
# Anzahl der Beobachtungen
anzahl = len(wallis_df)

# Teststatistik aus dem Kruskal-Wallis-Test
chi = stat  # das ist H

# Effektstärke w berechnen (nach Cohen)
w = np.sqrt(chi / anzahl)

# Ergebnis anzeigen
print(f"Effektstärke (w): {w:.2f}")

# 
Effektstärke (w): 0.85

$$ \begin{align} \text{Schwacher Effekt: } 0.10 &< w \le 0.30 \\ \text{Mittlerer Effekt: } 0.30 &< w \le 0.50 \\ \text{Starker Effekt: } 0.50 &< w \end{align} $$

Damit entspricht eine Effektstärke von 0.85 einem starken Effekt.

Eine Aussage¶

Der Kruskal-Wallis-Test bestätigt, dass die Sportlichkeit sich durch die Altersgruppe unterscheidet (Chi-Quadrat(2) = 21.22, p = .000).

Der anschliessend durchgeführte Post-hoc-Test (Tukey) zeigt, dass alle Gruppen "0-30" (Median = 13.5, n = 10)und "31-55" (Median = 9.2, n = 8), sowie "56+" (Median = 3.0, n = 9) signifikant unterscheiden (p < .05).

Die Effektstärke (w = .85) zeigt ein starken Effekt, sodass tatsächlich die Altersgruppe einen grossen Effekt auf die Ausdauer, gemessen in Minuten auf dem Laufband, hat.

H0 wird ablehnen.