Pakete einlesen¶

In [97]:
import pandas as pd                    # Datenverarbeitung
import seaborn as sns                 # Grafiken (Boxplots, Histogramme)
import matplotlib.pyplot as plt       # Diagramme
from scipy.stats import levene        # Levene-Test (Varianzgleichheit)
from statsmodels.formula.api import ols  # Lineare Modelle
import pingouin as pg                 # ANOVA, Welch-Test, Effektgrößen

Daten einlesen¶

In [98]:
# Excel-Datei laden
df = pd.read_excel("mehranova.xlsx")
df.columns = df.columns.str.strip()
df.columns = ["ID", "Geschlecht", "Alkohol", "Songs"]
print(df.head(5))
   ID Geschlecht Alkohol  Songs
0   1          m  gering      2
1   2          m  gering      3
2   3          m  gering      4
3   4          m  gering      2
4   5          m  gering      2

Hypothese¶

Haupteffekt A
H0:Es gibt keinen Unterschied zwischen dem Geschlecht und der gesungenen Anzahl an Songs.
H1:Es gibt einen Unterschied zwischen dem Geschlecht und der gesungenen Anzahl an Songs.

Haupteffekt B
H0:Es gibt einen Unterschied zwischen dem Alkoholgehalt und der gesungenen Anzahl an Songs.
H1:Es gibt einen Unterschied zwischen dem Alkoholgehalt und der gesungenen Anzahl an Songs.

Interaktion AxB
H1: Es gibt einen Unterschied zwischen dem Geschlecht, bzw. dem Alkoholgehalt des ersten Getränkes und der Anzahl der gesungenen Songs.
H0: Es gibt keinen Unterschied zwischen dem Geschlecht, bzw. dem Alkoholgehalt des ersten Getränkes und der Anzahl der gesungenen Songs.

Voraussetzungen für die mehrfaktorielle Varianzanalyse (ohne Messwiederholung)¶

Die abhängige Variable ist intervallskaliert -> ist gegeben, sogar absolutskaliert.

Die unabhängigen Variablen (Geschlecht und Alkoholgehalt) sind kategorial (nominal- oder ordinalskaliert) -> sind sie. Die durch die Faktoren gebildeten Gruppen sind unabhängig -> Entweder ist der Gast biologisch weiblich / männlich , bzw. der Alkoholgehalt des ersten Getränkes ist entweder 0.05 | 0.15 | 0.35.

Die abhängige Variablen ist normalverteilt innerhalb jeder der Gruppen. Ab 25 Probanden pro Gruppe sind Verletzungen dieser Voraussetzung unproblematisch-> siehe Histogramm

omogenität der Varianzen: Die Gruppen stammen aus Grundgesamtheiten mit annähernd identischen Varianzen der abhängigen Variablen -> siehe Levene-Test

Deskriptive Statistiken + Prüfung der Voraussetzung¶

Histogramm zur Prüfung der Normalvertweilung¶

Alkohol¶

In [99]:
# Histogramme für Songs, aufgeteilt nach Alkoholkonsum
g = sns.FacetGrid(df, col="Alkohol", hue="Alkohol", palette="Set2", col_wrap=3, sharex=True, sharey=True)
g.map_dataframe(sns.histplot, x="Songs", binwidth=1.5, kde=False, edgecolor="black")

# Achsentitel und Layout
g.set_axis_labels("Anzahl der Songs", "Anzahl")
g.set_titles(col_template="{col_name}")
g.add_legend()
plt.tight_layout()
plt.show()
No description has been provided for this image

Die Daten sind normalverteilt innerhalb der Gruppe Alkohol und Anzahl der gesungenen Songs.

In [100]:
# Histogramm nach Geschlecht gruppiert (Facetten)
g = sns.FacetGrid(df, col="Geschlecht", hue="Geschlecht", palette="Set2", col_wrap=2, sharex=True, sharey=True)
g.map_dataframe(sns.histplot, x="Songs", binwidth=3.3, kde=False, edgecolor="black")

# Achsentitel & Design
g.set_axis_labels("Anzahl der Songs", "Anzahl")
g.set_titles(col_template="{col_name}")
g.add_legend()
plt.tight_layout()
plt.show()
No description has been provided for this image

Die Daten sind normalverteilt innerhalb der Gruppe Geschlecht und Anzahl der gesungenen Songs, nicht sehr schön. Ab 25 Teilnehmer pro Gruppe ist das Testverfahren robust.

In [101]:
# Histogramm facettiert nach Alkohol und Geschlecht
g = sns.FacetGrid(df, row="Alkohol", col="Geschlecht", margin_titles=True, height=3.5)
g.map_dataframe(sns.histplot, x="Songs", bins=5, color="skyblue", edgecolor="black")

# Gestaltung
g.set_axis_labels("Anzahl der Songs", "Anzahl")
g.set_titles(row_template="{row_name}", col_template="{col_name}")
plt.tight_layout()
plt.show()
No description has been provided for this image

Ab 25 Teilnehmer pro Gruppe ist das Testverfahren robust. Jedoch gibt es Schwierigkeiten bei der Normalverteilung bei mittlerem Alkoholgehalt bei den weiblichen Barbesuchern.

Boxplot¶

In [102]:
# Boxplot: Songs nach Geschlecht, farblich gefüllt nach Alkohol
plt.figure(figsize=(8, 5))
sns.boxplot(data=df, x="Geschlecht", y="Songs", hue="Alkohol", palette="Set2")

#  Gestaltung
plt.title("Boxplot: Songs nach Geschlecht und Alkoholniveau")
plt.xlabel("Geschlecht")
plt.ylabel("Anzahl Songs")
plt.legend(title="Alkoholniveau")
sns.despine()
plt.tight_layout()
plt.show()
No description has been provided for this image

Deskriptive Statistiken¶

In [103]:
# Gruppierte deskriptive Statistik nach Alkohol
ergebnis = (
    df.groupby("Alkohol")["Songs"]
    .agg(Anzahl="count", Mittelwert="mean", Median="median", Standardabweichung="std")
    .round(2)
)

print(ergebnis)
         Anzahl  Mittelwert  Median  Standardabweichung
Alkohol                                                
gering       50        2.66     2.0                1.15
hoch         50       11.00    11.0                2.54
mittel       50        5.88     6.0                1.92

Bei geringem Alkoholgehalt werden 2.66 Songs im Schnitt gesungen (SD = 1.15, n = 50). Bei mittlerem Alkoholgehalt singen die Barbesucher 5.88 Songs im Durchschnitt (SD = 1.92, n = 50). Bei hoher Alkoholgehalt singen die Barbesucher deutlich mehr Songs - im Schnitt 11 Songs (SD = 2.54, n = 50).

In [104]:
# Gruppierte deskriptive Statistik nach Geschlecht
ergebnis = (
    df.groupby("Geschlecht")["Songs"]
    .agg(Anzahl="count", Mittelwert="mean", Median="median", Standardabweichung="std")
    .round(2)
)

print(ergebnis)
            Anzahl  Mittelwert  Median  Standardabweichung
Geschlecht                                                
m               75        6.44     7.0                2.93
w               75        6.59     5.0                4.78

Es zeichnet sich ein schwieriges Bild. Der Geschlechterunterschied ist nur minimal. Barbesucher singen 6.44 Songs im Schnitt, wohingegen Barbesucherinnen 6.59 Songs singen. Darüber hinaus zeigen auch die Standardabweichung und der Mittelwert, dass wahrscheinlich keine Signifikanz vorliegt.

In [105]:
# Gruppierte deskriptive Statistik nach Geschlecht und Alkohol
ergebnis = (
    df.groupby(["Geschlecht", "Alkohol"])["Songs"]
    .agg(Anzahl="count", Mittelwert="mean", Median="median", Standardabweichung="std")
    .round(2)
)

print(ergebnis)
                    Anzahl  Mittelwert  Median  Standardabweichung
Geschlecht Alkohol                                                
m          gering       25        3.24     3.0                1.16
           hoch         25        9.28     9.0                1.70
           mittel       25        6.80     7.0                1.76
w          gering       25        2.08     2.0                0.81
           hoch         25       12.72    13.0                2.03
           mittel       25        4.96     5.0                1.65

Bei geringer Alkoholgehalt singen die Männer (M = 3.24, SD = 1.164) mehr Songs als die Frauen (M = 2.08, SD = .81). Dies ist auch bei mittlerer Alkoholgehalt ebenfalls der Fall (Männer: M = 6.8, SD = 1.75; Frauen: M = 4.96, SD = .1.64). Bei hoher Alkoholgehalt ist es genau umgekehrt: Die Frauen (M = 12.72, SD = 2.03) singen deutlich mehr Songs als die Männer(M = 9.28, SD = 1.69).

Profildiagramm¶

In [106]:
# Linien-/Punktdiagramm mit Fehlerbalken (Konfidenzintervall)
plt.figure(figsize=(8, 5))
sns.pointplot(
    data=df,
    x="Alkohol",
    y="Songs",
    hue="Geschlecht",
    dodge=0.3,
    capsize=0.2,
    err_kws={"linewidth": 1},      
    palette="Set2",
    markers="o",
    linestyles="-"
)


# Achsentitel & Layout
plt.xlabel("Alkohol")
plt.ylabel("Anzahl der Songs")
plt.title("Mittelwerte mit Fehlerbalken (± CI)")
sns.despine()
plt.tight_layout()
plt.show()
No description has been provided for this image

Levene -Test¶

Prüfung der Varianzhomogenität mit dem Levene-Test¶

Der Levene-Test prüft, ob die Streuung (Varianz) in den Gruppen gleich ist.
Dabei gilt die Nullhypothese, dass alle Gruppen die gleiche Varianz aufweisen.

  • Ist das Testergebnis nicht signifikant (p > 0.05), kann angenommen werden, dass Varianzhomogenität vorliegt. In diesem Fall ist eine wichtige Voraussetzung für die Varianzanalyse erfüllt.

  • Ist der Levene-Test hingegen signifikant (p ≤ 0.05), bedeutet das, dass sich die Gruppenvarianzen deutlich unterscheiden. Dies verletzt eine zentrale Annahme der Varianzanalyse.

Umgang mit Varianzverletzungen¶

  • Leichte Abweichungen von der Varianzhomogenität sind meist nicht kritisch, wenn die Gruppen:

    • ausreichend groß sind und
    • ähnlich große Gruppengrößen aufweisen.
  • Problematisch wird es bei:

    • stark ungleichen Varianzen und
    • deutlich ungleichen Gruppengrößen.
      In diesem Fall kann der klassische F-Test der ANOVA verzerrt sein.

Alternative Verfahren¶

Wenn die Varianzhomogenität verletzt ist, bieten sich robustere Alternativen zur klassischen ANOVA an:

  • Welch-Test
  • Brown-Forsythe-Test

Beide Tests sind angepasste F-Tests, die weniger empfindlich gegenüber ungleichen Varianzen sind und daher in solchen Situationen zuverlässigere Ergebnisse liefern.

In [107]:
# Gruppen für Kombination Alkohol * Geschlecht
gruppen = df.groupby(["Alkohol", "Geschlecht"])["Songs"].apply(list).tolist()

# Levene-Test
stat, p = levene(*gruppen, center="mean")

# Freiheitsgrade berechnen
k = len(gruppen)
n = sum(len(g) for g in gruppen)
df_between = k - 1
df_within = n - k

# Ergebnis ausgeben
print("Levene-Test für Songs ~ Alkohol * Geschlecht")
print(f"F = {stat:.2f}")
print(f"p-Wert = {p:.4f}")
print(f"Freiheitsgrade: df = {df_between}, {df_within}")
Levene-Test für Songs ~ Alkohol * Geschlecht
F = 5.63
p-Wert = 0.0001
Freiheitsgrade: df = 5, 144

Im vorliegenden Beispiel ist der Levene-Test signifikant (F(5,144) = 5.626, p < .000 ), so dass von Varianzheterogenität ausgegangen werden kann. Da die Varianzen leider nicht gleich sind, ist es zu empfehlen eine Korrektur mithilfe des Welch-Tests durchzuführen.

Entscheiungsregeln¶

Mit Welch-Korrektur: p < 0.05 => Ergebnis Signifikant --> Varianzen heterogen

Ohne Welch-Korrektur: p > 0.05 => Ergebnis nicht Signifikant --> Varianzen homogen --> H0 mit Annahme Var1=Var2=... -> Var_n wird angenommen

Ergebnisse der mehrfaktorielle Varianzanalyse (ohne Messwiederholung)¶

*Keine Welch-Korrektur * - ist der Standard

In [108]:
# Faktorvariablen sicherstellen
df["Geschlecht"] = df["Geschlecht"].astype("category")
df["Alkohol"] = df["Alkohol"].astype("category")

# Modell: Haupteffekte + Interaktion
modell = ols("Songs ~ C(Geschlecht) * C(Alkohol)", data=df).fit()

# ANOVA (Type III – wie in R)
anova_ergebnis = sm.stats.anova_lm(modell, typ=3)

# Ergebnis anzeigen
print(anova_ergebnis)
                              sum_sq     df           F        PR(>F)
Intercept                 262.440000    1.0  106.322755  5.176985e-19
C(Geschlecht)              16.820000    1.0    6.814315  9.999384e-03
C(Alkohol)                460.880000    2.0   93.358542  1.002818e-26
C(Geschlecht):C(Alkohol)  206.253333    2.0   41.779878  4.911637e-15
Residual                  355.440000  144.0         NaN           NaN

*mit Welch-Korrektur * ist zur Kontrolle gedacht

Es liegt eine Verletzung der Varianzhomogenität vor(siehe Levene-Test). Daher sollte ein robusterer Test berücksichtigt werden (* mit welch* ). Allerdings bieten sich hier keine weitgehend akzeptierten Verfahren an. Daher wird der Standard weitergeführt.

In [109]:
ergebnis = pg.welch_anova(data=df, dv="Songs", between="Geschlecht")
print(ergebnis)
       Source  ddof1       ddof2         F     p-unc       np2
0  Geschlecht      1  122.724419  0.051224  0.821323  0.000346

Für den Faktor Geschlecht wird kein Haupteffekt ersichtlich. Die Anzahl der gesungenen Songs scheint demnach unabhängig vom Geschlecht zu sein (F(1,122.72) = .0512, p = .8213).

In [110]:
# Welch-ANOVA: Songs nach Alkoholgruppen (ungleich große Gruppen, ungleiche Varianzen erlaubt)
ergebnis = pg.welch_anova(data=df, dv="Songs", between="Alkohol")

print(ergebnis)
    Source  ddof1      ddof2           F         p-unc       np2
0  Alkohol      2  88.418511  238.446986  2.391095e-36  0.758736

Es gibt einen Haupteffekt der Alkoholgehalt auf die Anzahl der gesungenen Songs (F(2,88.419) = 238.45, p = .000). Das bedeutet, dass Gäste der Karaoke-Bar in Abhängigkeit des Alkoholgehalt unterschiedlich viele Songs singen.

In [111]:
# R-Code als Kommentar – bitte in Python übersetzen
# 
# AXBwelch <- oneway.test(Songs~Alkohol*Geschlecht, data = mehranova, var.equal = F)
# AXBwelch 
# Welch-ANOVA für Songs nach Alkoholgruppen
ergebnis_alkohol = pg.welch_anova(data=df, dv="Songs", between="Alkohol")
print("Welch-ANOVA: Songs ~ Alkohol")
print(ergebnis_alkohol)

# Welch-ANOVA für Songs nach Geschlecht
ergebnis_geschlecht = pg.welch_anova(data=df, dv="Songs", between="Geschlecht")
print("\nWelch-ANOVA: Songs ~ Geschlecht")
print(ergebnis_geschlecht)
Welch-ANOVA: Songs ~ Alkohol
    Source  ddof1      ddof2           F         p-unc       np2
0  Alkohol      2  88.418511  238.446986  2.391095e-36  0.758736

Welch-ANOVA: Songs ~ Geschlecht
       Source  ddof1       ddof2         F     p-unc       np2
0  Geschlecht      1  122.724419  0.051224  0.821323  0.000346

Hinweis: Da pg.welch_anova() keine Interaktionen unterstützt, führen wir zwei getrennte Welch-ANOVAs durch.

Alternative mit Interaktion¶

In [112]:
# Zwei-Faktor-ANOVA mit Interaktion 
modell = ols("Songs ~ C(Alkohol) * C(Geschlecht)", data=df).fit()
anova = sm.stats.anova_lm(modell, typ=3)
print(anova)
                              sum_sq     df           F        PR(>F)
Intercept                 262.440000    1.0  106.322755  5.176985e-19
C(Alkohol)                460.880000    2.0   93.358542  1.002818e-26
C(Geschlecht)              16.820000    1.0    6.814315  9.999384e-03
C(Alkohol):C(Geschlecht)  206.253333    2.0   41.779878  4.911637e-15
Residual                  355.440000  144.0         NaN           NaN

Der Interaktionsterm von Alkoholgehalt und Geschlecht auf die Anzahl der gesungenen Songs ist signifikant (F(2,144) = 41,78, p = .000). Der Effekt von Alkoholgehalt hängt demnach zu einem gewissen Teil vom Geschlecht ab.

Post- Hoc - Test¶

Ein signifikanter Haupteffekt oder eine Interaktion zeigt, dass ein statistisch bedeutsamer Unterschied zwischen Gruppen besteht.
Allerdings ist dadurch noch nicht ersichtlich, zwischen welchen Gruppen der Unterschied liegt – vor allem, wenn ein Faktor mehr als zwei Stufen hat.

Wenn ein Faktor nur zwei Ausprägungen besitzt, ist klar: Bei einem signifikanten F-Test unterscheiden sich genau diese beiden Gruppen.
Sind es mehr als zwei Stufen, müssen zusätzliche Post-hoc-Tests durchgeführt werden, um die genaue Struktur der Unterschiede zu identifizieren.

Im aktuellen Beispiel zeigen sich sowohl ein signifikanter Haupteffekt des Alkoholgehalts als auch eine Interaktion.
Post-hoc-Tests helfen hier weiter – allerdings sind Interaktionen in vielen Statistikpaketen schwieriger zu testen. In Python lässt sich das für Haupteffekte gut umsetzen.

Anzahl der Paarvergleiche¶

Die Anzahl aller möglichen Paarvergleiche ergibt sich aus:

$ \text{Anzahl der Vergleiche} = \frac{k(k - 1)}{2} $

mit $k$ = Anzahl der Gruppen.

Beispiel:

  • Bei $k = 3$: $ \frac{3(3 - 1)}{2} = 3 $ Vergleiche
  • Bei $k = 6$: $ \frac{6(6 - 1)}{2} = 15 $ Vergleiche

Problem: Alpha-Fehler-Inflation¶

Mehrere Vergleiche führen zu einer erhöhten Fehlerwahrscheinlichkeit:

$ P(\text{kein Fehler}) = 0{,}95^{15} \approx 0{,}4632 $

$\Rightarrow$

$ P(\text{mindestens ein Fehler}) = 1 - 0{,}4632 = 0{,}5367 $

Das bedeutet: Über 53 % Wahrscheinlichkeit für mindestens einen Fehler bei 15 Tests.
Diese kumulierte Fehlerwahrscheinlichkeit nennt man Familywise Error Rate (FWER).

Lösung: Tukey-HSD-Test¶

Der Tukey-Test (Tukey Honestly Significant Difference) korrigiert für Mehrfachvergleiche und erlaubt es, bei einem globalen $\alpha = 0{,}05$ zu bleiben.

In Python kann der Tukey-Test z. B. mit statsmodels durchgeführt werden.

In [113]:
# Post-Hoc-Test mit TukeyHSD (für die Faktor-Kombination)
df["Gruppe"] = df["Geschlecht"].astype(str) + " / " + df["Alkohol"].astype(str)

# Tukey-HSD für alle Faktor-Kombinationen
tukey = pairwise_tukeyhsd(endog=df["Songs"], groups=df["Gruppe"], alpha=0.05)
print(tukey)
    Multiple Comparison of Means - Tukey HSD, FWER=0.05     
============================================================
  group1     group2   meandiff p-adj   lower   upper  reject
------------------------------------------------------------
m / gering   m / hoch     6.04    0.0  4.7564  7.3236   True
m / gering m / mittel     3.56    0.0  2.2764  4.8436   True
m / gering w / gering    -1.16 0.1013 -2.4436  0.1236  False
m / gering   w / hoch     9.48    0.0  8.1964 10.7636   True
m / gering w / mittel     1.72 0.0022  0.4364  3.0036   True
  m / hoch m / mittel    -2.48    0.0 -3.7636 -1.1964   True
  m / hoch w / gering     -7.2    0.0 -8.4836 -5.9164   True
  m / hoch   w / hoch     3.44    0.0  2.1564  4.7236   True
  m / hoch w / mittel    -4.32    0.0 -5.6036 -3.0364   True
m / mittel w / gering    -4.72    0.0 -6.0036 -3.4364   True
m / mittel   w / hoch     5.92    0.0  4.6364  7.2036   True
m / mittel w / mittel    -1.84 0.0008 -3.1236 -0.5564   True
w / gering   w / hoch    10.64    0.0  9.3564 11.9236   True
w / gering w / mittel     2.88    0.0  1.5964  4.1636   True
  w / hoch w / mittel    -7.76    0.0 -9.0436 -6.4764   True
------------------------------------------------------------

Wie vermutet, zeichnet sich bei Geschlecht keine signifikant ab. Die Alkoholgehaltskombinationen unterscheiden sich signifikant.

Für das vorliegende Beispiel wird ersichtlich, dass sich der Alkoholgehalt und das Geschlecht bezüglich der gesungenen Songs signifikant unterscheiden (p < .05). Jedoch kann kein Unterschied für das Geschlecht mit einer geringem Alkoholgehalt von 0.05 Prozent festgestellt werden (p < .05). Es können 5 Gruppen gebildet werden.

  1. Was ist sig und was nicht? 14 von 15 Vergleichen sind sig. (p < .05). Nicht sig. ist w:gering-m:gering (p = .1013)

  2. Generalisierbarkeit /Unabhängigkeit - global?

  • mittel weiblich
  • mittel männlich
  • hoch weiblich
  • hoch männlich

Vier Gruppen sind unabhängige / generalisierbar.

  1. Gruppenbildung?

Es können 5 Gruppen gebildet werden.

  1. Interaktion?

Bei geringerem Alkoholgehalt lag Männer (M = 3.24, SD = 1.164) über jenem der Frauen (M = 2.08, SD = .81), allerdings konnte der Unterschied im Post-Hoc nicht bestätigt werden. Bei mittlerer Alkoholgehalt (Männer: M = 6.8, SD = 1.75; Frauen: M = 4.96, SD = .1.64) konnte der Mittelwertsunterschied bestätigt werden. Bei hohem Alkoholgehalt verhält es sich genau andersrum - Frauen sangen (M = 12.72, SD = 2.03) deutlich mehr Songs als die Männer (M = 12.72, SD = 2.03). Das Ergebnis konnte bestätigt werden.

Games - Howell¶

In [114]:
# Games-Howell für Geschlecht
gh_geschlecht = pg.pairwise_gameshowell(data=df, dv="Songs", between="Geschlecht")
print("Games-Howell: Geschlecht")
print(gh_geschlecht)
Games-Howell: Geschlecht
   A  B  mean(A)   mean(B)      diff        se         T          df  \
0  m  w     6.44  6.586667 -0.146667  0.648028 -0.226328  122.724419   

       pval    hedges  
0  0.821323 -0.036772  
In [115]:
# Games-Howell für Alkohol
gh_alkohol = pg.pairwise_gameshowell(data=df, dv="Songs", between="Alkohol")
print("Games-Howell: Alkohol")
print(gh_alkohol)
Games-Howell: Alkohol
        A       B  mean(A)  mean(B)  diff        se          T         df  \
0  gering    hoch     2.66    11.00 -8.34  0.394462 -21.142739  68.400056   
1  gering  mittel     2.66     5.88 -3.22  0.317194 -10.151505  80.217246   
2    hoch  mittel    11.00     5.88  5.12  0.450524  11.364557  91.299164   

   pval    hedges  
0   0.0 -4.196104  
1   0.0 -2.014723  
2   0.0  2.255472  
In [116]:
df["interaktion"] = df["Geschlecht"].astype(str) + " / " + df["Alkohol"].astype(str)

# Games-Howell für Interaktion
gh_interaktion = pg.pairwise_gameshowell(data=df, dv="Songs", between="interaktion")
print("Games-Howell: Interaktion")
print(gh_interaktion)
Games-Howell: Interaktion
             A           B  mean(A)  mean(B)   diff        se          T  \
0   m / gering    m / hoch     3.24     9.28  -6.04  0.411501 -14.677960   
1   m / gering  m / mittel     3.24     6.80  -3.56  0.421426  -8.447506   
2   m / gering  w / gering     3.24     2.08   1.16  0.284019   4.084237   
3   m / gering    w / hoch     3.24    12.72  -9.48  0.468330 -20.242121   
4   m / gering  w / mittel     3.24     4.96  -1.72  0.403154  -4.266357   
5     m / hoch  m / mittel     9.28     6.80   2.48  0.488262   5.079238   
6     m / hoch  w / gering     9.28     2.08   7.20  0.376121  19.142799   
7     m / hoch    w / hoch     9.28    12.72  -3.44  0.529276  -6.499442   
8     m / hoch  w / mittel     9.28     4.96   4.32  0.472582   9.141279   
9   m / mittel  w / gering     6.80     2.08   4.72  0.386954  12.197835   
10  m / mittel    w / hoch     6.80    12.72  -5.92  0.537029 -11.023616   
11  m / mittel  w / mittel     6.80     4.96   1.84  0.481248   3.823390   
12  w / gering    w / hoch     2.08    12.72 -10.64  0.437569 -24.316163   
13  w / gering  w / mittel     2.08     4.96  -2.88  0.366970  -7.848062   
14    w / hoch  w / mittel    12.72     4.96   7.76  0.522813  14.842786   

           df          pval    hedges  
0   42.518492  0.000000e+00 -4.086346  
1   41.694370  2.097334e-09 -2.351787  
2   42.882457  2.457921e-03  1.137052  
3   38.241104  0.000000e+00 -5.635410  
4   43.228323  1.404675e-03 -1.187755  
5   47.942354  8.675852e-05  1.414061  
6   34.462034  0.000000e+00  5.329358  
7   46.518055  7.165350e-07 -1.809446  
8   47.955542  6.603229e-11  2.544934  
9   33.824447  9.134915e-13  3.395879  
10  47.015498  1.825207e-13 -3.068976  
11  47.797715  4.836468e-03  1.064432  
12  31.485426  0.000000e+00 -6.769623  
13  35.047556  4.588965e-08 -2.184902  
14  46.013026  2.997602e-15  4.132234  

Das par. Eta-Quadrat¶

Das partielle Eta-Quadrat (partielles η2) ist in Abbildung ausgegeben.

$$\eta^2_{partial A}= \frac {QS_A}{QS_A+QS_{inn}}$$

$$\eta^2_{partial B}= \frac {QS_B}{QS_B+QS_{inn}}$$

$$\eta^2_{partial AxB}= \frac {QS_{AxB}}{QS_{AxB}+QS_{inn}}$$

$$\eta_A= \frac {QS_A}{QS_{total}}$$ $$\eta_B= \frac {QS_B}{QS_{total}}$$ $$\eta_{AxB}= \frac {QS_{AxB}}{QS_{total}}$$

In [117]:
# Modell fitten
modell = ols("Songs ~ C(Geschlecht) * C(Alkohol)", data=df).fit()

# Modell in Pingouin analysieren (braucht keine separate ANOVA)
anova_pg = pg.anova(data=df, dv="Songs", between=["Geschlecht", "Alkohol"], detailed=True)

# Zeigt auch: F, p, η², np² (partial eta squared)

print(anova_pg[["Source", "np2"]])
                 Source       np2
0            Geschlecht  0.002264
1               Alkohol  0.832688
2  Geschlecht * Alkohol  0.367199
3              Residual       NaN

Im vorliegenden Beispiel ist der Effekt des Geschlechts nicht signifikant. Daher wird dessen partielles Eta-Quadrat nicht betrachtet. Für die Alkoholgehalt beträgt das partielle Eta-Quadrat .833. Das heisst, die Alkoholgehalt erklärt 83% derjenigen Fehlervariation, die das Modell hätte, wäre Alkoholgehalt nicht im Modell. Das partielle Eta-Quadrat der Interaktion beträgt .367 und erklärt daher 3% der ohne die Interaktion ungeklärten Variation.

Berechnung der Effektstärke¶

$$f=\sqrt\frac{\eta^{2}}{1-\eta^{2}}=\sqrt\frac{eta^{2}}{1-eta^{2}}$$

Effektstärke fürs Geschlecht¶

In [118]:
# ANOVA mit Effektgrößen
anova = pg.anova(data=df, dv="Songs", between=["Geschlecht", "Alkohol"], detailed=True)

# Effektgröße f für Geschlecht berechnen aus np2 (partielles Eta²)
eta_gender = anova.query("Source == 'Geschlecht'")["np2"].values[0]
f_gender = (eta_gender / (1 - eta_gender))**0.5

print(f"Effektstärke (f) für Geschlecht: {f_gender:.3f}")
Effektstärke (f) für Geschlecht: 0.048

Effektstärke fürs Alkoholgehalt¶

In [119]:
# Effektstärke f für Alkohol aus partieller Eta² berechnen
eta_alkohol = anova.query("Source == 'Alkohol'")["np2"].values[0]
f_alkohol = (eta_alkohol / (1 - eta_alkohol))**0.5

print(f"Effektstärke fürs Alkoholgehalt: f = {f_alkohol:.3f}")
Effektstärke fürs Alkoholgehalt: f = 2.231

Effektstärke für die Interaktion¶

In [120]:
# Effektstärke f für die Interaktion berechnen
eta_interaktion = anova.query("Source == 'Geschlecht * Alkohol'")["np2"].values[0]
f_interaktion = (eta_interaktion / (1 - eta_interaktion))**0.5

print(f"Effektstärke für die Interaktion: f = {f_interaktion:.3f}")
Effektstärke für die Interaktion: f = 0.762

Um zu beurteilen, wie gross dieser Effekt ist, kann man sich an der Einteilung von Cohen (1988) orientieren:

$$ \begin{align} \text{Schwacher Effekt: } 0.10 &< ||f|| < 0.25 \\ \text{Schwacher bis mittlerer Effekt: } 0.25 &= ||f|| \\ \text{Mittlerer Effekt: } 0.25 &< ||f|| < 0.40 \\ \text{Mittlerer bis starker Effekt: }0.40 &= ||f|| \\ \text{Starker Effekt: } 0.40 &< ||f|| \end{align} $$

Damit entsprechen die Effektstärken von 2.23 und 0.76 einem starken Effekt. Für Geschlecht liegt natürlich kein Effekt vor - es sei nur der Vollständigkeit erwähnt.

Eine Aussage¶

Hypothesen¶

Es zeigt sich, dass es keinen Unterschied zwichen dem Geschlecht und der Anzahl an gesungenen Songs allein gibt (F(1,122.72) = .0512, p = .8213). H0 für den Haupteffekt A wird angenommen.

Allerdings je nach Alkoholgehalt des ersten Getränkes gibt es einen Unterschied zwischen den gesungenen Songs (F(2,88.419) = 238.45, p = .000). H0 für den Haupteffekt B wird verworfen.

Der Interaktionsterm von Alkoholgehalt und Geschlecht auf die Anzahl der gesungenen Songs ist signifikant (F(2,144) = 41,78, p = .000) Der Effekt von Alkoholgehalt hängt demnach zu einem gewissen Teil vom Geschlecht ab. H0 für den Interaktion AxB wird verworfen.

Post- Hoc - Test¶

Der PostHoc wurde mit Tukey durchgeführt. Für Geschlecht zeigte sich keine signifikant ab (p = 0.101). Es zeigt, dass sich alle Gruppen für Alkoholgehalt signifikant unterscheiden: Gering (M = 2.66 ,SD = 1.15, N=50), mittlere Alkoholniveau (M = 5.88, SD = 1.92, n=50) und hohe Alkoholniveau (M = 11, SD = 2.54, n=50).

Zudem zeigt sich eine signifikante Interaktion von Geschlecht und Alkoholgehalt auf die Anzahl der gesungenen Songs.Dies weist darauf hin, dass sich Alkoholgehalt je nach Geschlecht unterschiedlich auswirkt.

Bei geringerem Alkoholgehalt lag Männer (M = 3.24, SD = 1.164) über jenem der Frauen (M = 2.08, SD = .81), allerdings konnte der Unterschied im Post-Hoc nicht bestätigt werden. Bei mittlerer Alkoholgehalt (Männer: M = 6.8, SD = 1.75; Frauen: M = 4.96, SD = .1.64) konnte der Mittelwertsunterschied bestätigt werden.

Bei hohem Alkoholgehalt verhält es sich genau andersrum - Frauen sangen (M = 12.72, SD = 2.03) deutlich mehr Songs als die Männer (M = 12.72, SD = 2.03). Das Ergebnis konnte bestätigt werden.

Effektstärke¶

Die Effektstärken sind sowohl für den Haupteffekt der Alkoholgehalt (f = 2.23) als auch für die Interaktion (f = 0,76) nach Cohen (1988) ein starker Effekt