Pakete einlesen¶
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¶
# 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¶
# 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()
Die Daten sind normalverteilt innerhalb der Gruppe Alkohol und Anzahl der gesungenen Songs.
# 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()
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.
# 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()
Ab 25 Teilnehmer pro Gruppe ist das Testverfahren robust. Jedoch gibt es Schwierigkeiten bei der Normalverteilung bei mittlerem Alkoholgehalt bei den weiblichen Barbesuchern.
Boxplot¶
# 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()
Deskriptive Statistiken¶
# 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).
# 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.
# 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¶
# 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()
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.
# 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
# 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.
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).
# 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.
# 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¶
# 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.
# 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.
Was ist sig und was nicht? 14 von 15 Vergleichen sind sig. (p < .05). Nicht sig. ist w:gering-m:gering (p = .1013)
Generalisierbarkeit /Unabhängigkeit - global?
- mittel weiblich
- mittel männlich
- hoch weiblich
- hoch männlich
Vier Gruppen sind unabhängige / generalisierbar.
- Gruppenbildung?
Es können 5 Gruppen gebildet werden.
- 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¶
# 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
# 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
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}}$$
# 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¶
# 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¶
# 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¶
# 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