ANOVA och post-hoc-test
Jämför medelvärden mellan tre eller fler grupper med envägs-ANOVA och genomför Tukey HSD för att identifiera vilka par som skiljer sig åt.
ANOVA och post-hoc-test är en gratis lektion i Pandas & NumPy Academy på CoddyKit. Detta är lektion 4 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Pandas & NumPy Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Pandas & NumPy Academy innehåller totalt 4 lektioner.
Varför inte flera t-test?
När ni jämför medelvärden mellan tre eller fler grupper ökar flera t-test felfrekvensen av typ I (frekvensen av falska positiva). Om ni testar grupp A mot B, A mot C och B mot C med α=0,05 för varje test blir den totala sannolikheten för ett falskt positivt resultat cirka 14 %, inte 5 %. Variansanalys (ANOVA) löser detta genom att testa alla grupper samtidigt i ett enda test, vilket håller frekvensen av falska positiva exakt vid α. En ANOVA ersätter alla parvisa t-test för den övergripande frågan: ”Skiljer sig någon grupp från de andra?”
Envägs-ANOVA: den stora bilden
Envägs-ANOVA testar om medelvärdena för tre eller fler grupper skiljer sig signifikant. Den delar upp den totala variansen i varians mellan grupper (som förklaras av grupptillhörighet) och varians inom grupper (slumpmässigt brus). F-statistikan är kvoten mellan dessa: F = (varians mellan grupper) / (varians inom grupper). Ett stort F-värde innebär att skillnaderna mellan grupperna är stora i förhållande till bruset, vilket ger ett litet p-värde. Nollhypotesen är H₀: alla gruppmedelvärden är lika.
import numpy as np
from scipy import stats
np.random.seed(0)
# Three product variants with different conversion rates
group_a = np.random.normal(10.0, 2.0, 50) # baseline
group_b = np.random.normal(11.5, 2.0, 50) # slightly better
group_c = np.random.normal(13.0, 2.0, 50) # clearly better
f_stat, p = stats.f_oneway(group_a, group_b, group_c)
print(f'F-statistic: {f_stat:.3f}')
print(f'p-value: {p:.6f}')
print('At least one group differs?', 'Yes' if p < 0.05 else 'No')ANOVA med Pandas DataFrames
I praktiken finns era data i en DataFrame i långt format, där en kolumn innehåller gruppetiketten och en annan innehåller mätvärdet. Extrahera varje grupp som en Series och skicka dem till stats.f_oneway(). Om ni har en DataFrame i brett format (en kolumn per grupp) kan ni i stället skicka varje kolumn direkt. Funktionen accepterar valfritt antal positionsargument, vilket gör det enkelt att utöka analysen till 4, 5 eller fler grupper.
import pandas as pd
import numpy as np
from scipy import stats
np.random.seed(1)
df = pd.DataFrame({
'group': ['A']*40 + ['B']*40 + ['C']*40 + ['D']*40,
'score': np.concatenate([
np.random.normal(70, 10, 40),
np.random.normal(75, 10, 40),
np.random.normal(80, 10, 40),
np.random.normal(72, 10, 40)
])
})
groups = [group['score'].values
for _, group in df.groupby('group')]
f, p = stats.f_oneway(*groups)
print(f'F={f:.3f}, p={p:.4f}')
print(df.groupby('group')['score'].mean().round(2))ANOVA-antaganden
Envägs-ANOVA förutsätter: (1) oberoende — observationerna är oberoende av varandra, (2) normalfördelning — residualerna är ungefär normalfördelade inom varje grupp (metoden är robust för stora stickprov tack vare centrala gränsvärdessatsen), och (3) homoskedasticitet — lika varianser i alla grupper. Testa antagandet om lika varianser med Levenes test (stats.levene(*groups)). Om varianserna är olika kan ni i stället använda Welchs ANOVA, som finns i biblioteket pingouin eller kan beräknas med statsmodels.
import numpy as np
from scipy import stats
np.random.seed(0)
g1 = np.random.normal(10, 2, 40)
g2 = np.random.normal(12, 2, 40)
g3 = np.random.normal(11, 8, 40) # Much higher variance
# Test equal variances
stat_l, p_l = stats.levene(g1, g2, g3)
print(f'Levene\'s test: stat={stat_l:.3f}, p={p_l:.4f}')
if p_l < 0.05:
print('Warning: Unequal variances! Standard ANOVA may be unreliable.')
print('Consider Welch\'s ANOVA or Kruskal-Wallis.')Kruskal–Wallis: icke-parametrisk ANOVA
När ANOVA-antagandena inte är uppfyllda (icke-normalfördelade data, små stickprov eller olika varianser) är Kruskal–Wallis-testet det icke-parametriska alternativet. Det testar om gruppernas fördelningar skiljer sig genom att omvandla data till rangordningar och jämföra rangsummor. Använd scipy.stats.kruskal(*groups). Nollhypotesen är att alla grupper har samma fördelning (vilket motsvarar ett test av lika medianer när fördelningarna har samma form). Testet är alltid giltigt, men har lägre styrka än ANOVA när antagandena är uppfyllda.
import numpy as np
from scipy import stats
np.random.seed(0)
# Non-normal data: customer satisfaction scores (1-10 scale)
g1 = np.random.choice(range(1, 11), 50, p=[0.05]*10)
g2 = np.random.choice(range(1, 11), 50, p=[0.02, 0.03, 0.05, 0.08, 0.12, 0.15, 0.20, 0.15, 0.12, 0.08])
g3 = np.random.choice(range(1, 11), 50, p=[0.15, 0.15, 0.15, 0.15, 0.10, 0.10, 0.08, 0.05, 0.04, 0.03])
stat, p = stats.kruskal(g1, g2, g3)
print(f'Kruskal-Wallis: H={stat:.3f}, p={p:.4f}')
print('Groups differ?', 'Yes' if p < 0.05 else 'No')Post-hoc-test: varför de behövs
En signifikant ANOVA visar att minst ett gruppmedelvärde skiljer sig, men inte vilka par som skiljer sig. Post-hoc-test utför alla parvisa jämförelser och korrigerar samtidigt för multipla jämförelser. Det vanligaste är Tukeys ärliga signifikanta skillnad (Tukey HSD), som kontrollerar den familjevisa felfrekvensen exakt vid α. Det finns i statsmodels.stats.multicomp.pairwise_tukeyhsd(). Kör post-hoc-test endast efter en signifikant ANOVA — inte som en resultatjakt när resultaten inte är signifikanta.
import numpy as np
import pandas as pd
from scipy import stats
from statsmodels.stats.multicomp import pairwise_tukeyhsd
np.random.seed(0)
data = np.concatenate([
np.random.normal(70, 10, 40),
np.random.normal(80, 10, 40),
np.random.normal(75, 10, 40)
])
groups = ['A']*40 + ['B']*40 + ['C']*40
# First confirm ANOVA is significant
f, p = stats.f_oneway(*[data[i*40:(i+1)*40] for i in range(3)])
print(f'ANOVA p={p:.4f}')
if p < 0.05:
print('Post-hoc Tukey HSD:')
print(pairwise_tukeyhsd(data, groups, alpha=0.05))Tolka resultat från Tukey HSD
Tukey HSD-tabellen har en rad för varje par av grupper. De kolumner ni främst bör fokusera på är: meandiff (skillnaden mellan parens medelvärden), lower och upper (95-procentigt konfidensintervall för skillnaden) samt reject (True om paret skiljer sig signifikant vid α). Ett konfidensintervall som inte innehåller noll innebär att paret skiljer sig signifikant. Grupper vars konfidensintervall överlappar varandra skiljer sig inte signifikant åt.
import numpy as np
from statsmodels.stats.multicomp import pairwise_tukeyhsd
np.random.seed(42)
data = np.concatenate([
np.random.normal(10, 2, 60), # Group A
np.random.normal(13, 2, 60), # Group B (clearly higher)
np.random.normal(10.5, 2, 60) # Group C (barely different from A)
])
groups = ['A']*60 + ['B']*60 + ['C']*60
result = pairwise_tukeyhsd(data, groups, alpha=0.05)
print(result)
# A-B: reject=True (B is higher)
# A-C: reject=False (barely different)
# B-C: reject=True (B is higher)Bonferroni- och Benjamini–Hochberg-korrigeringar
Som alternativ till Tukey HSD kan ni tillämpa en Bonferroni-korrigering på parvisa t-test: kör alla t-test och justera sedan tröskelvärdet till α/k, där k är antalet jämförelser. Detta är konservativt — verkliga skillnader kan missas. Korrigeringen Benjamini–Hochbergs falska upptäcktsfrekvens (FDR) är mindre konservativ: den kontrollerar den förväntade andelen falska upptäckter och tillåter fler sanna positiva resultat, på bekostnad av något fler falska positiva. Använd statsmodels.stats.multitest.multipletests(p_values, method='fdr_bh').
import numpy as np
from scipy import stats
from statsmodels.stats.multitest import multipletests
np.random.seed(0)
groups = [np.random.normal(10 + i*1.5, 2, 40) for i in range(4)]
names = ['A', 'B', 'C', 'D']
# All pairwise t-tests
p_values = []
pairs = []
for i in range(len(groups)):
for j in range(i+1, len(groups)):
_, p = stats.ttest_ind(groups[i], groups[j])
p_values.append(p)
pairs.append(f'{names[i]}-{names[j]}')
# Benjamini-Hochberg correction
reject, adj_p, _, _ = multipletests(p_values, method='fdr_bh')
for pair, p, ap, r in zip(pairs, p_values, adj_p, reject):
print(f'{pair}: raw_p={p:.4f}, adj_p={ap:.4f}, significant={r}')Effektstorlek för ANOVA: eta-kvadrat
För ANOVA är det motsvarande måttet till Cohens d eta-kvadrat (η²): andelen av den totala variansen som förklaras av grupptillhörighet. η² = SS_between / SS_total. Värden: < 0,01 är liten, 0,06 är medelstor och > 0,14 är stor. Partiell eta-kvadrat (η²_p) är vanligare i publicerad forskning: den dividerar endast med summan av variansen mellan grupperna och felvariansen, inte med den totala variansen. Beräkna alltid η² tillsammans med ANOVA:s p-värde för att visa om effekten är praktiskt betydelsefull, inte bara statistiskt detekterbar.
import numpy as np
from scipy import stats
np.random.seed(0)
groups = [
np.random.normal(10, 2, 50),
np.random.normal(12, 2, 50),
np.random.normal(14, 2, 50)
]
all_data = np.concatenate(groups)
grand_mean = all_data.mean()
ss_between = sum(len(g) * (g.mean() - grand_mean)**2 for g in groups)
ss_total = sum((x - grand_mean)**2 for g in groups for x in g)
eta_sq = ss_between / ss_total
f, p = stats.f_oneway(*groups)
print(f'F={f:.3f}, p={p:.4f}')
print(f'Eta-squared: {eta_sq:.4f} ({eta_sq:.1%} of variance explained)')Översikt över tvåvägs-ANOVA
Tvåvägs-ANOVA utökar envägs-ANOVA till två kategoriska faktorer samtidigt. Exempelvis kan ni testa om provresultat skiljer sig åt beroende på både undervisningsmetod och elevernas erfarenhetsnivå. Metoden testar också interaktionseffekten: beror effekten av undervisningsmetoden på erfarenhetsnivån? Tvåvägs-ANOVA implementeras i statsmodels med ols('score ~ C(method) + C(level) + C(method):C(level)', data=df).fit() och anova_lm(model). Detta utgör en grund för mer avancerad experimentdesign.
import pandas as pd
import numpy as np
from statsmodels.formula.api import ols
from statsmodels.stats.anova import anova_lm
np.random.seed(0)
df = pd.DataFrame({
'method': ['trad']*60 + ['active']*60,
'level': ['beginner']*30 + ['advanced']*30 + ['beginner']*30 + ['advanced']*30,
'score': (np.random.normal(70, 8, 30).tolist() +
np.random.normal(80, 8, 30).tolist() +
np.random.normal(75, 8, 30).tolist() +
np.random.normal(88, 8, 30).tolist())
})
model = ols('score ~ C(method) + C(level) + C(method):C(level)', data=df).fit()
print(anova_lm(model, typ=2)[['F', 'PR(>F)']].round(4))ANOVA med upprepade mätningar
ANOVA med upprepade mätningar används när samma individer mäts flera gånger — exempelvis när reaktionstiden mäts vid tidpunkterna 0, 30 och 60 minuter efter en behandling. Det är en generalisering av det parade t-testet till flera grupper. Genom att ta hänsyn till individuella skillnader har metoden större statistisk styrka än envägs-ANOVA. I Python kan ni använda pingouin.rm_anova() eller pg.pairwise_tests() för post-hoc-analysen. När sfäricitetsantagandet inte är uppfyllt (testas med Mauchlys test) använder ni p-värden korrigerade med Greenhouse–Geisser.
import pandas as pd
import numpy as np
# Example structure for repeated measures (pingouin library)
np.random.seed(0)
subjects = list(range(20)) * 3 # 20 subjects, 3 time points
timepoints = ['T0']*20 + ['T1']*20 + ['T2']*20
scores = np.concatenate([
np.random.normal(50, 10, 20), # baseline
np.random.normal(55, 10, 20), # improved
np.random.normal(60, 10, 20) # further improved
])
df_rm = pd.DataFrame({'subject': subjects, 'time': timepoints, 'score': scores})
print(df_rm.groupby('time')['score'].mean().round(2))
# Use pingouin.rm_anova(data=df_rm, dv='score', within='time', subject='subject')Snabbkontroll
Testa er förståelse av begreppen inom dataanalys från den här lektionen.
Sammanfattning av lektionen
I den här lektionen har ni lärt er att scipy.stats.f_oneway() testar om några grupper skiljer sig åt i medelvärde samtidigt som den totala felfrekvensen av typ I kontrolleras, att post-hoc-test (Tukey HSD) identifierar vilka specifika par som skiljer sig efter en signifikant ANOVA och att Kruskal–Wallis är det icke-parametriska alternativet när antaganden om normalfördelning eller lika varianser inte är uppfyllda. Nästa steg är capstone-projektet, där ni integrerar alla färdigheter i en komplett datapipeline från början till slut.
Lär dig Python med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 30
- Lektioner
- 120
Vanliga frågor
Är lektionen ”ANOVA och post-hoc-test” gratis?
Ja – hela texten till ”ANOVA och post-hoc-test” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Pandas & NumPy Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i Pandas & NumPy Academy innehåller totalt 4 lektioner.
Vad lär jag mig i ”ANOVA och post-hoc-test”?
Jämför medelvärden mellan tre eller fler grupper med envägs-ANOVA och genomför Tukey HSD för att identifiera vilka par som skiljer sig åt. Ni övar på Pandas & NumPy Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig Pandas & NumPy Academy?
Du behöver inga förkunskaper. Utbildningen i Pandas & NumPy Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 4 av 4.
Hur lång tid tar lektionen ”ANOVA och post-hoc-test”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här Pandas & NumPy Academy-lektionen?
Ja. Varje Pandas & NumPy Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- Deskriptiv statistik och normalitetstest
- T-test för jämförelse av medelvärden
- Chi-två-test för oberoende
- ANOVA och post-hoc-test