Post-Hoc-Tests¶
Mit Hilfe eines multiplen Mittelwertvergleichs auf der Basis von Post-Hoc-Tests lassen sich signifikante Mittelwertunterschiede nach Homogenitätskriterien beurteilen.
Eine Gruppe von Mittelwerten, die durch den F-Test einer ANOVA als inhomogen ausgewiesen wurde, kann durch Post-Hoc-Verfahren in homogene Teilgruppen gegliedert werden. Dadurch klären multiple Mittelwertvergleiche auch, welche Faktorstufen im Einzelnen den signifikanten Effekt in einer ANOVA produziert haben.
Im Gegensatz zu den Post-Hoc-Tests würde ein t-Test die statistische Signifikanz von Mittelwertunterschieden oft überschätzen. Angenommen, Sie bilden 20 Stichproben mit je 10 Zufallszahlen und berechnen die 20 Mittelwerte der Stichproben. Dann vergleichen Sie die Gruppe mit dem höchsten Mittelwert mit der Gruppe mit dem kleinsten Mittelwert. Ein t-Test für unabhängige Stichproben würde beim Prüfen dieses Unterschieds auf Signifikanz einen Effekt anzeigen, auch wenn dieser rein zufällig ist.
Pakete importieren¶
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from scipy import stats
from statsmodels.stats.multicomp import pairwise_tukeyhsd
import statsmodels.api as sm
from statsmodels.formula.api import ols
import numpy as np
import pandas as pd
import statsmodels.api as sm
from statsmodels.formula.api import ols
from statsmodels.stats.multicomp import MultiComparison
from plotnine import ggplot, aes, stat_summary, labs, theme_classic
import pandas as pd
import numpy as np
from IPython.display import display
import numpy as np
import pandas as pd
from plotnine import *
from plotnine.stats import stat_summary
pip install plotnine.stats
# Zufallsgenerator setzen (für Reproduzierbarkeit)
np.random.seed(42)
# Für jede Gruppe 30 Zufallswerte mit unterschiedlichen Mittelwerten
group_A = np.random.normal(loc=5.0, scale=1, size=30)
group_B = np.random.normal(loc=5.5, scale=1, size=30)
group_C = np.random.normal(loc=6.5, scale=1, size=30)
group_D = np.random.normal(loc=7.0, scale=1, size=30)
group_E = np.random.normal(loc=7.5, scale=1, size=30)
# Zusammenfügen in einen DataFrame
data = pd.DataFrame({
'wert': np.concatenate([group_A, group_B, group_C, group_D, group_E]),
'gruppe': ['A'] * 30 + ['B'] * 30 + ['C'] * 30 + ['D'] * 30 + ['E'] * 30
})
# Vorschau: erste 5 Zeilen anzeigen
print(data.head())
wert gruppe 0 5.496714 A 1 4.861736 A 2 5.647689 A 3 6.523030 A 4 4.765847 A
# Gruppierte Statistik berechnen
summary = (
data.groupby("gruppe")["wert"]
.agg(["mean", "count", "std"])
.assign(se=lambda df: df["std"] / np.sqrt(df["count"]))
.reset_index()
)
# Plot erstellen
plt.figure(figsize=(6, 4))
# Punkte (Mittelwerte) – ohne die veralteten Argumente
sns.pointplot(
data=summary,
x="gruppe",
y="mean",
errorbar=None, # Fehlerbalken übernehmen wir gleich manuell
color="black",
markers="o"
)
# Linie zwischen den Punkten (ersetzt das frühere join=True)
plt.plot(summary["gruppe"], summary["mean"], color="black", linestyle="-")
# Fehlerbalken (Standardfehler)
plt.errorbar(
x=summary["gruppe"],
y=summary["mean"],
yerr=summary["se"],
fmt='none',
capsize=4,
color="black",
linewidth=0.8
)
# Achsenbeschriftungen & Stil
plt.xlabel("x")
plt.ylabel("y")
plt.title("Mittelwerte mit Fehlerbalken")
sns.despine()
plt.tight_layout()
plt.show()
parametisch¶
Tukey’s Test (gleiche Gruppen)¶
Der Tukey-Test ist ein Post-hoc-Verfahren, das alle möglichen Paarvergleiche zwischen Gruppen durchführt. Im Vergleich zu anderen verfügbaren Tests lässt er sich hinsichtlich seiner Strenge als moderat einordnen – er liegt also etwa in der Mitte zwischen eher konservativen und eher liberalen Verfahren.
# Einfaktorielle ANOVA
model = ols('wert ~ gruppe', data=data).fit()
anova_table = sm.stats.anova_lm(model, typ=2)
print(anova_table)
sum_sq df F PR(>F) gruppe 142.731848 4.0 39.334329 2.828571e-22 Residual 131.539794 145.0 NaN NaN
# Tukey-HSD Post-Hoc-Test
tukey = pairwise_tukeyhsd(endog=data['wert'], groups=data['gruppe'], alpha=0.05)
print(tukey)
Multiple Comparison of Means - Tukey HSD, FWER=0.05 =================================================== group1 group2 meandiff p-adj lower upper reject --------------------------------------------------- A B 0.567 0.149 -0.1124 1.2463 False A C 1.701 0.0 1.0217 2.3804 True A D 2.1679 0.0 1.4886 2.8472 True A E 2.5932 0.0 1.9138 3.2725 True B C 1.134 0.0001 0.4547 1.8134 True B D 1.6009 0.0 0.9216 2.2802 True B E 2.0262 0.0 1.3469 2.7055 True C D 0.4669 0.3228 -0.2125 1.1462 False C E 0.8922 0.0036 0.2128 1.5715 True D E 0.4253 0.4193 -0.254 1.1046 False ---------------------------------------------------
# Visualisierung
sns.boxplot(x='gruppe', y='wert', data=data)
plt.title('Gruppenvergleich')
plt.show()
Bonferroni (ungleiche Gruppen)¶
Die Bonferroni-Korrektur zählt zu den einfachsten Methoden zur Kontrolle der Alphafehler-Kumulierung bei multiplen Tests. Sie ist besonders konservativ – das heißt, sie senkt die Wahrscheinlichkeit, fälschlich einen Effekt zu finden (Fehler 1. Art). Je mehr Tests durchgeführt werden, desto strenger wird das Verfahren. Gerade bei vielen Vergleichen kann die Bonferroni-Korrektur daher als zu streng gelten, da sie auch echte Effekte übersehen kann. Trotz dieser Einschränkung ist sie weit verbreitet, da sie leicht anzuwenden und rechnerisch unkompliziert ist.
# ANOVA (optional, für Kontext)
model = ols('wert ~ gruppe', data=data).fit()
anova_table = sm.stats.anova_lm(model, typ=2)
print("ANOVA:\n", anova_table)
# Bonferroni-Post-Hoc-Test
mc = MultiComparison(data['wert'], data['gruppe'])
result = mc.allpairtest(sm.stats.ttest_ind, method='bonf')[0]
print("\nBonferroni-Korrektur (ungepaarte t-Tests):\n")
print(result)
ANOVA: sum_sq df F PR(>F) gruppe 142.731848 4.0 39.334329 2.828571e-22 Residual 131.539794 145.0 NaN NaN Bonferroni-Korrektur (ungepaarte t-Tests): Test Multiple Comparison ttest_ind FWER=0.05 method=bonf alphacSidak=0.01, alphacBonf=0.005 ============================================== group1 group2 stat pval pval_corr reject ---------------------------------------------- A B -2.3981 0.0197 0.1972 False A C -6.956 0.0 0.0 True A D -9.2835 0.0 0.0 True A E -10.4175 0.0 0.0 True B C -4.5655 0.0 0.0003 True B D -6.7392 0.0 0.0 True B E -8.0179 0.0 0.0 True C D -1.9007 0.0623 0.6232 False C E -3.4272 0.0011 0.0113 True D E -1.7012 0.0943 0.9425 False ----------------------------------------------
non-parametrisch¶
# Beispieldaten erzeugen
np.random.seed(42)
data1 = pd.DataFrame({
"name": np.repeat(["A", "B", "C", "D", "E"], 5),
"value": np.concatenate([
np.random.normal(loc=8, scale=5, size=5),
np.random.normal(loc=10, scale=1, size=5),
np.random.normal(loc=28, scale=1, size=5),
np.random.normal(loc=31, scale=4, size=5),
np.random.normal(loc=37, scale=1, size=5)
])
})
print(data1.head(5))
name value 0 A 10.483571 1 A 7.308678 2 A 11.238443 3 A 15.615149 4 A 6.829233
# Plot erstellen (funktionierende Version!)
p = (
ggplot(data1, aes(x='name', y='value', group=1)) +
stat_summary(fun_y=np.mean, geom="point", size=3) +
stat_summary(fun_y=np.mean, geom="line") +
stat_summary(fun_data="mean_cl_normal", geom="errorbar", width=0.2, size=0.25) +
labs(x="x", y="y") +
theme_classic()
)
# Plot anzeigen
display(p)
Dunn-Test¶
Der Dunn-Test ist ein nichtparametrisches Post-hoc-Verfahren, das eingesetzt wird, um Gruppenunterschiede nach einem signifikanten Kruskal-Wallis-Test genauer zu untersuchen. Er erlaubt paarweise Vergleiche zwischen Gruppen, ohne die Annahme einer Normalverteilung vorauszusetzen, und eignet sich daher besonders für ordinalskalierte oder schiefe Daten. Zur Kontrolle des kumulierten Alphafehlers kann der Dunn-Test mit Korrekturverfahren wie Holm, Bonferroni oder Benjamini-Hochberg kombiniert werden. Damit stellt er eine robuste Alternative zu parametrischen Post-hoc-Tests dar, wenn deren Voraussetzungen nicht erfüllt sind.
import scikit_posthocs as sp
import pandas as pd
# DataFrame: data1 mit Spalten "name" (Gruppe) und "value" (Messwert)
sp.posthoc_dunn(data1, val_col='value', group_col='name', p_adjust='holm')
A | B | C | D | E | |
---|---|---|---|---|---|
A | 1.000000 | 1.000000 | 0.221805 | 0.182173 | 0.001313 |
B | 1.000000 | 1.000000 | 0.221805 | 0.221805 | 0.001978 |
C | 0.221805 | 0.221805 | 1.000000 | 1.000000 | 0.375181 |
D | 0.182173 | 0.221805 | 1.000000 | 1.000000 | 0.375181 |
E | 0.001313 | 0.001978 | 0.375181 | 0.375181 | 1.000000 |