ANOVA og post-hoc-tester
Sammenlign gjennomsnitt på tvers av tre eller flere grupper med enveis-ANOVA, og bruk Tukey HSD til å identifisere hvilke par som skiller seg fra hverandre.
ANOVA og post-hoc-tester er en gratis leksjon i Pandas & NumPy Academy på CoddyKit. Dette er leksjon 4 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Pandas & NumPy Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Pandas & NumPy Academy inneholder totalt 4 leksjoner.
Hvorfor ikke flere t-tester?
Når De sammenligner gjennomsnittet i tre eller flere grupper, øker flere t-tester type I-feilraten (raten av falske positive). Hvis De tester gruppe A mot B, A mot C og B mot C med α=0.05 for hver test, blir den samlede sannsynligheten for et falskt positivt resultat omtrent 14 %, ikke 5 %. Variansanalyse (ANOVA) løser dette ved å teste alle gruppene samtidig i én enkelt test, slik at raten av falske positive holdes nøyaktig på α. Én ANOVA erstatter alle parvise t-tester for det overordnede spørsmålet: «Er det noen forskjell mellom gruppene?»
Enveis-ANOVA: Hovedbildet
Enveis-ANOVA tester om gjennomsnittene i tre eller flere grupper er signifikant forskjellige. Den deler den totale variansen inn i varians mellom grupper (forklart av gruppetilhørighet) og varians innenfor grupper (tilfeldig støy). F-statistikken er forholdet mellom disse: F = (varians mellom grupper) / (varians innenfor grupper). En stor F-verdi betyr at gruppeforskjellene er store sammenlignet med støyen, noe som gir en liten p-verdi. Nullhypotesen er H₀: alle gruppenes gjennomsnitt er like.
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 praksis ligger dataene Deres i en DataFrame i langt format, der én kolonne inneholder gruppeetiketten og en annen inneholder målingen. Hent ut hver gruppe som en Series, og send dem til stats.f_oneway(). Hvis De har en DataFrame i bredt format (én kolonne per gruppe), kan De alternativt sende hver kolonne direkte. Funksjonen godtar et valgfritt antall posisjonsargumenter, noe som gjør det enkelt å utvide analysen til 4, 5 eller flere 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-forutsetninger
Enveis-ANOVA forutsetter: (1) uavhengighet — observasjonene er uavhengige av hverandre; (2) normalitet — residualene er tilnærmet normalfordelte innenfor hver gruppe (metoden er robust for store utvalg takket være CLT); (3) homoskedastisitet — lik varians i alle grupper. Test variansforutsetningen med Levenes test (stats.levene(*groups)). Hvis variansene er ulike, kan De i stedet bruke Welchs ANOVA, som er tilgjengelig i biblioteket pingouin eller kan beregnes 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: Ikke-parametrisk ANOVA
Når ANOVA-forutsetningene ikke er oppfylt (ikke-normalfordelte fordelinger, små utvalg eller ulike varianser), er Kruskal-Wallis-testen det ikke-parametriske alternativet. Den tester om fordelingene i gruppene er forskjellige ved å konvertere dataene til rangeringer og sammenligne rangerte summer. Bruk scipy.stats.kruskal(*groups). Nullhypotesen er at alle gruppene har samme fordeling (tilsvarende testing av like medianer når fordelingene har samme form). Testen er alltid gyldig, men har mindre styrke enn ANOVA når forutsetningene er oppfylt.
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')Ettertester: Hvorfor De trenger dem
En signifikant ANOVA forteller Dem at minst ett gruppesnitt er forskjellig, men ikke hvilke par som er forskjellige. Ettertester utfører alle parvise sammenligninger samtidig som det korrigeres for multiple sammenligninger. Den mest populære er Tukeys ærlige signifikante forskjell (Tukey HSD), som kontrollerer den familiekloke feilraten nøyaktig på α. Den er tilgjengelig i statsmodels.stats.multicomp.pairwise_tukeyhsd(). Kjør ettertester bare etter en signifikant ANOVA — ikke som en leteaksjon når resultatene ikke er signifikante.
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))Tolking av Tukey HSD-resultater
Tukey HSD-tabellen har én rad for hvert par av grupper. Kolonnene De bør fokusere på, er: meandiff (gjennomsnittsforskjellen mellom paret), lower og upper (95 % konfidensintervall for forskjellen), samt reject (True hvis paret er signifikant forskjellig ved α). Et konfidensintervall som ikke inkluderer null, betyr at paret er signifikant forskjellig. Grupper med overlappende konfidensintervaller er ikke signifikant forskjellige fra hverandre.
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- og Benjamini-Hochberg-korrigeringer
Som et alternativ til Tukey HSD kan De bruke en Bonferroni-korrigering på parvise t-tester: kjør alle t-testene, og juster deretter terskelen til α/k, der k er antallet sammenligninger. Dette er konservativt — det kan føre til at reelle forskjeller ikke oppdages. Korrigeringen Benjamini-Hochberg False Discovery Rate (FDR) er mindre konservativ: Den kontrollerer den forventede andelen falske oppdagelser og tillater flere sanne positive resultater, på bekostning av litt flere falske positive. Bruk 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}')Effektstørrelse for ANOVA: Eta-kvadrat
For ANOVA er det tilsvarende målet til Cohens d eta-kvadrat (η²): andelen av den totale variansen som forklares av gruppetilhørighet. η² = SS_between / SS_total. Verdier: < 0.01 er liten, 0.06 er middels, og > 0.14 er stor. Partielt eta-kvadrat (η²_p) er vanligere i publisert forskning: Det deler bare på gruppevariansen pluss feilvariansen, ikke på totalvariansen. Beregn alltid η² sammen med ANOVA-p-verdien for å vise om effekten er praktisk betydningsfull, ikke bare statistisk påvisbar.
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)')Oversikt over toveis-ANOVA
Toveis-ANOVA utvider enveis-ANOVA til to kategoriske faktorer samtidig. De kan for eksempel teste om eksamensresultater varierer med både undervisningsmetode og studentenes erfaringsnivå samtidig. Den tester også interaksjonseffekten: Avhenger effekten av undervisningsmetoden av erfaringsnivået? Toveis-ANOVA implementeres i statsmodels ved hjelp av ols('score ~ C(method) + C(level) + C(method):C(level)', data=df).fit() og anova_lm(model). Dette danner grunnlaget for mer avansert eksperimentell utforming.
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 gjentatte målinger
ANOVA med gjentatte målinger brukes når de samme forsøkspersonene måles flere ganger — for eksempel ved testing av reaksjonstid på tidspunktene 0, 30 og 60 minutter etter en behandling. Dette er en generalisering av den parede t-testen til flere grupper. Ved å ta hensyn til individuelle forskjeller har metoden større styrke enn enveis-ANOVA. I Python kan De bruke pingouin.rm_anova() eller pg.pairwise_tests() til etteranalysen. Når sfærisiteten ikke er oppfylt (testet med Mauchlys test), bør De bruke p-verdier korrigert med Greenhouse-Geisser-metoden.
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')Hurtigsjekk
Test forståelsen Deres av konseptene innen dataanalyse fra denne leksjonen.
Oppsummering av leksjonen
I denne leksjonen lærte De at scipy.stats.f_oneway() tester om noen grupper har forskjellige gjennomsnitt, samtidig som den samlede type I-feilraten kontrolleres, at ettertester (Tukey HSD) identifiserer hvilke spesifikke par som er forskjellige etter en signifikant ANOVA, og at Kruskal-Wallis er det ikke-parametriske alternativet når forutsetningene om normalitet eller lik varians ikke er oppfylt. Deretter begynner vi på avslutningsprosjektet, der alle ferdighetene integreres i en komplett datapipeline fra start til slutt.
Lær deg Python med en AI-veileder – gratis
Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.
- Kurs
- 30
- Leksjoner
- 120
Ofte stilte spørsmål
Er leksjonen «ANOVA og post-hoc-tester» gratis?
Ja – hele teksten i «ANOVA og post-hoc-tester» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av Pandas & NumPy Academy-kurset, kan du oppgradere til CoddyKit PRO. Kurset i Pandas & NumPy Academy inneholder totalt 4 leksjoner.
Hva lærer jeg i «ANOVA og post-hoc-tester»?
Sammenlign gjennomsnitt på tvers av tre eller flere grupper med enveis-ANOVA, og bruk Tukey HSD til å identifisere hvilke par som skiller seg fra hverandre. Du øver på Pandas & NumPy Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.
Trenger jeg erfaring for å begynne med Pandas & NumPy Academy?
Ingen tidligere erfaring er nødvendig. Pandas & NumPy Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 4 av 4.
Hvor lang tid tar leksjonen «ANOVA og post-hoc-tester»?
De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.
Kan jeg skrive og kjøre kode i denne Pandas & NumPy Academy-leksjonen?
Ja. Alle Pandas & NumPy Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.
Alle leksjonene i dette kurset
- Deskriptiv statistikk og normalitetstesting
- T-tester for sammenligning av gjennomsnitt
- Kikvadrattest for uavhengighet
- ANOVA og post-hoc-tester