Pandas & NumPy Academy · Lektion

Chi-i-anden-test for uafhængighed

Test, om to kategoriske variabler er uafhængige, ved hjælp af chi2_contingency på en krydstabel med hyppigheder.

Lektion 3 af 413 trin

Chi-i-anden-test for uafhængighed er en gratis Pandas & NumPy Academy-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Pandas & NumPy Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Pandas & NumPy Academy-kurset indeholder 4 lektioner i alt.

Test af relationer mellem kategoriske variabler

Chi-i-anden-testen for uafhængighed tester, om to kategoriske variabler er statistisk uafhængige, eller om der er en sammenhæng mellem dem. For eksempel: »Er kundefrafald uafhængigt af abonnementstype?« eller »Er produktpræference uafhængig af aldersgruppe?« I modsætning til t-test, som sammenligner numeriske gennemsnit, sammenligner chi-i-anden-test observerede frekvenser i en krydstabel med de frekvenser, vi ville forvente, hvis variablerne var uafhængige.

Oprettelse af en krydstabel med Pandas

En krydstabel (også kaldet en krydstabulering) viser antallet af observationer for hver kombination af to kategoriske variabler. pd.crosstab(df['var1'], df['var2']) opretter denne tabel direkte fra en DataFrame. Hver celle indeholder antallet af observationer, hvor rækkekategorien og kolonnekategorien optræder sammen. Dette er inputtet til scipy.stats.chi2_contingency().

import pandas as pd
import numpy as np

np.random.seed(0)
df = pd.DataFrame({
    'subscription': np.random.choice(['free', 'basic', 'pro'], 300),
    'churned': np.random.choice(['yes', 'no'], 300, p=[0.3, 0.7])
})

# Build contingency table
ct = pd.crosstab(df['subscription'], df['churned'])
print(ct)
print()
print('Row totals:', ct.sum(axis=1).to_dict())

Kørsel af chi2_contingency()

scipy.stats.chi2_contingency(observed) tager den observerede krydstabel (som et NumPy-array eller en Pandas DataFrame) og returnerer fire værdier: chi-i-anden-statistikken, p-værdien, frihedsgraderne og tabellen over forventede frekvenser. Nulhypotesen er H₀: the two variables are independent. Hvis p ≤ 0.05, forkaster vi uafhængighed og konkluderer, at der er en statistisk signifikant sammenhæng.

import pandas as pd
import numpy as np
from scipy import stats

np.random.seed(0)
df = pd.DataFrame({
    'tier': np.random.choice(['free', 'basic', 'pro'], 300),
    'churned': np.random.choice(['yes', 'no'], 300, p=[0.3, 0.7])
})

ct = pd.crosstab(df['tier'], df['churned'])
chi2, p, dof, expected = stats.chi2_contingency(ct)

print(f'Chi-squared: {chi2:.3f}')
print(f'p-value: {p:.4f}')
print(f'Degrees of freedom: {dof}')
print('Association significant?', 'Yes' if p < 0.05 else 'No')

Forståelse af forventede frekvenser

De forventede frekvenser viser, hvordan celletallene ville se ud, hvis de to variabler var helt uafhængige. For hver celle gælder expected = (row_total × column_total) / grand_total. Chi-i-anden-statistikken måler summen af de kvadrerede forskelle mellem observerede og forventede tal, normaliseret med de forventede tal. En stor chi-i-anden-værdi betyder, at de observerede tal afviger kraftigt fra uafhængighed; en lille chi-i-anden-værdi betyder, at dataene er forenelige med uafhængighed.

import pandas as pd
import numpy as np
from scipy import stats

observed = pd.DataFrame({
    'converted': [50, 30],
    'not_converted': [150, 170]
}, index=['variant', 'control'])

chi2, p, dof, expected = stats.chi2_contingency(observed)
print('Observed:')
print(observed)
print()
print('Expected (under independence):')
print(pd.DataFrame(expected,
                   index=observed.index,
                   columns=observed.columns).round(1))

Frihedsgrader i chi-i-anden-testen

For en krydstabel med r rækker og c kolonner er antallet af frihedsgrader df = (r-1) × (c-1). En tabel på 2×2 har df=1; en tabel på 3×4 har df=6. Den kritiske værdi for chi-i-anden stiger med antallet af frihedsgrader: for df=1 ved α=0,05 er den kritiske værdi ≈ 3,84; for df=6 ved α=0,05 er den kritiske værdi ≈ 12,6. Funktionen chi2_contingency håndterer dette automatisk, men kendskab til formlen hjælper dig med at forstå, hvorfor chi-i-anden-værdier fra større tabeller skal være større for at være signifikante.

from scipy import stats

# Critical values of chi-squared for alpha=0.05
for df in [1, 2, 3, 4, 6, 9]:
    critical = stats.chi2.ppf(0.95, df=df)
    print(f'df={df}: critical value = {critical:.3f}')

Forudsætningen om en minimal forventet frekvens

Chi-i-anden-testen er kun pålidelig, når alle forventede frekvenser er mindst 5 (nogle kilder siger mindst 1, og at højst 20 % må være under 5). Små forventede tal gør chi-i-anden-approksimationen unøjagtig. Når denne forudsætning ikke er opfyldt, skal du bruge Fishers eksakte test (scipy.stats.fisher_exact()) for 2×2-tabeller eller samle sjældne kategorier for at øge de forventede tal. Undersøg altid matricen med forventede frekvenser, som returneres af chi2_contingency.

import numpy as np
from scipy import stats

# Table with small expected counts
observed = np.array([[2, 3], [100, 95]])
chi2, p, dof, expected = stats.chi2_contingency(observed)

print('Expected frequencies:')
print(expected)
print('Min expected:', expected.min())

if expected.min() < 5:
    print('Warning: Expected frequency < 5. Use Fisher\'s exact test.')
    odds_ratio, p_fisher = stats.fisher_exact(observed)
    print(f'Fisher\'s exact p: {p_fisher:.4f}')

Fishers eksakte test for små stikprøver

scipy.stats.fisher_exact(table) beregner den eksakte sandsynlighed for at observere den givne 2×2-tabel (eller en endnu mere ekstrem tabel) under nulhypotesen om uafhængighed uden nogen approksimation. Den er altid gyldig uanset stikprøvestørrelse eller celletal — p-værdien er eksakt, ikke approksimeret. Ulempen er beregningsomkostningen: Funktionen gennemgår alle mulige tabeller, hvilket gør den langsom ved store totaler. For 2×2-tabeller med et celletal under 5 skal du altid foretrække Fishers eksakte test frem for chi-i-anden-testen.

import numpy as np
from scipy import stats

# Small clinical trial: treatment vs. outcome
observed = np.array([
    [3, 12],   # treated: 3 improved, 12 did not
    [1, 18]    # control: 1 improved, 18 did not
])

odds_ratio, p = stats.fisher_exact(observed, alternative='two-sided')
print(f'Odds ratio: {odds_ratio:.3f}')
print(f'p-value: {p:.4f}')
print('Association significant?', 'Yes' if p < 0.05 else 'No')

Måling af sammenhængens styrke: Cramérs V

Ligesom Cohens d for t-test måler chi-i-anden-statistikken alene ikke sammenhængens styrke — den bliver påvirket af stikprøvestørrelsen. Cramérs V normaliserer chi-i-anden til en skala fra 0 til 1: 0 betyder ingen sammenhæng, og 1 betyder perfekt sammenhæng. V = sqrt(chi2 / (n × min(r-1, c-1))). Retningslinjer: < 0,1 er svag, 0,1–0,3 er moderat, og > 0,3 er stærk. Rapportér altid Cramérs V sammen med p-værdien for at give et fuldstændigt billede.

import pandas as pd
import numpy as np
from scipy import stats

np.random.seed(0)
df = pd.DataFrame({
    'tier': np.random.choice(['free', 'basic', 'pro'], 500),
    'churn': np.random.choice(['yes', 'no'], 500, p=[0.3, 0.7])
})
ct = pd.crosstab(df['tier'], df['churn'])

chi2, p, dof, _ = stats.chi2_contingency(ct)
n = ct.sum().sum()
min_dim = min(ct.shape[0]-1, ct.shape[1]-1)
cramers_v = np.sqrt(chi2 / (n * min_dim))

print(f'chi2={chi2:.3f}, p={p:.4f}')
print(f'Cramer\'s V: {cramers_v:.4f}')
print('Association strength:', 'strong' if cramers_v > 0.3 else 'moderate' if cramers_v > 0.1 else 'weak')

Chi-i-anden-test for tilpasning

En anden anvendelse af chi-i-anden er tilpasningstesten: Den tester, om observerede frekvenser stemmer overens med en hypotetisk fordeling. For eksempel: »Er terningekast uniformt fordelt?« eller »Følger trafikken på vores websted det forventede mønster for ugedagene?« Brug scipy.stats.chisquare(f_obs, f_exp), hvor f_exp er de forventede frekvenser. Nulhypotesen er, at dataene følger den angivne fordeling.

import numpy as np
from scipy import stats

# Observed: counts for each weekday over 70 weeks
observed = np.array([980, 1050, 1020, 1080, 1120, 850, 900])
# Expected: uniform distribution
expected = np.full(7, observed.sum() / 7)

chi2, p = stats.chisquare(f_obs=observed, f_exp=expected)
print('Observed:', observed)
print('Expected (uniform):', expected.round(0))
print(f'chi2={chi2:.3f}, p={p:.4f}')
print('Uniform?', 'Yes' if p > 0.05 else 'No - some days significantly busier')

Brug af pd.crosstab med normalisering

Når du rapporterer resultater fra chi-i-anden-testen, er det nyttigt at vise proportioner i stedet for rå tal, så læserne kan se den relative sammenhæng. pd.crosstab(var1, var2, normalize='index') viser rækkeproportionen (hvilken andel af hver rækkekategori der falder i hver kolonne). normalize='columns' viser kolonneproportioner. Hvis du sammenligner disse proportioner visuelt, før du kører testen, kan det hjælpe dig med at forudse sammenhængens retning og fortolke resultatet i den rette kontekst.

import pandas as pd
import numpy as np

np.random.seed(0)
df = pd.DataFrame({
    'tier': np.random.choice(['free', 'basic', 'pro'], 300),
    'churned': np.random.choice(['yes', 'no'], 300, p=[0.3, 0.7])
})

# Row-proportions: churn rate within each tier
prop_table = pd.crosstab(df['tier'], df['churned'], normalize='index')
print('Churn rate by tier:')
print((prop_table * 100).round(1))

Chi-i-anden-test i A/B-test

Chi-i-anden-testen er den almindelige test til konverteringsrater i A/B-test. Opret en 2×2-krydstabel med rækker = (kontrolgruppe, variant) og kolonner = (konverteret, ikke konverteret). Chi-i-anden-testen fortæller dig, om konverteringsraten er signifikant forskellig mellem grupperne. Det svarer til en z-test for to proportioner ved store stikprøver. Ved små stikprøver (et forventet celletal < 5) skal du i stedet bruge Fishers eksakte test.

import numpy as np
from scipy import stats

# A/B test: control vs. variant, conversions vs. non-conversions
control_conv = 45
control_total = 500
variant_conv = 63
variant_total = 500

contingency = np.array([
    [control_conv, control_total - control_conv],
    [variant_conv, variant_total - variant_conv]
])

chi2, p, dof, expected = stats.chi2_contingency(contingency)
print(f'Control rate: {control_conv/control_total:.1%}')
print(f'Variant rate: {variant_conv/variant_total:.1%}')
print(f'p-value: {p:.4f}')
print('Variant significantly better?', 'Yes' if p < 0.05 else 'No')

Hurtigt tjek

Test din forståelse af begreberne inden for dataanalyse fra denne lektion.

Opsummering af lektionen

I denne lektion lærte du: pd.crosstab() + chi2_contingency() tester, om to kategoriske variabler er uafhængige, baseret på observerede og forventede frekvenser, Fishers eksakte test er det sikre alternativ, når de forventede tal er under 5, og Cramérs V måler sammenhængens styrke uafhængigt af stikprøvestørrelsen. Dernæst sammenligner vi gennemsnit på tværs af tre eller flere grupper med ANOVA og efterfølgende test.

Gratis at komme i gang

Lær Python med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
30
Lektioner
120

Ofte stillede spørgsmål

Er lektionen “Chi-i-anden-test for uafhængighed” gratis?

Ja — hele teksten til “Chi-i-anden-test for uafhængighed” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Pandas & NumPy Academy-kurset, skal du opgradere til CoddyKit PRO. Pandas & NumPy Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Chi-i-anden-test for uafhængighed”?

Test, om to kategoriske variabler er uafhængige, ved hjælp af chi2_contingency på en krydstabel med hyppigheder. Du øver dig i Pandas & NumPy Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Pandas & NumPy Academy?

Der kræves ingen tidligere erfaring. Pandas & NumPy Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.

Hvor lang tid tager lektionen “Chi-i-anden-test for uafhængighed”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Pandas & NumPy Academy-lektion?

Ja. Alle Pandas & NumPy Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Deskriptiv statistik og test af normalitet
  2. T-tests til sammenligning af middelværdier
  3. Chi-i-anden-test for uafhængighed
  4. ANOVA og post-hoc-test
← Tilbage til Pandas & NumPy Academy