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¶

In [1]:
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
In [2]:
pip install plotnine.stats
In [3]:
# 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
In [12]:
# 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()
No description has been provided for this image

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.

In [5]:
# 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
In [6]:
# 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
---------------------------------------------------
In [7]:
# Visualisierung
sns.boxplot(x='gruppe', y='wert', data=data)
plt.title('Gruppenvergleich')
plt.show()
No description has been provided for this image

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.

In [8]:
# 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¶

In [9]:
# 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
In [10]:
# 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)
No description has been provided for this image

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.

In [11]:
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')
Out[11]:
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