Pandas & NumPy Academy · Lekcja

Test chi-kwadrat niezależności

Sprawdź, czy dwie zmienne kategorialne są niezależne, używając chi2_contingency na tabeli częstości utworzonej za pomocą crosstab.

Lekcja 3 z 413 kroki

Test chi-kwadrat niezależności to bezpłatna lekcja Pandas & NumPy Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Pandas & NumPy Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Pandas & NumPy Academy zawiera 4 lekcji w sumie.

Badanie zależności między zmiennymi kategorialnymi

Test niezależności chi-kwadrat sprawdza, czy dwie zmienne kategorialne są statystycznie niezależne, czy też występuje między nimi zależność. Na przykład: „Czy rezygnacja klienta z usługi jest niezależna od poziomu subskrypcji?” albo „Czy preferencje dotyczące produktu są niezależne od grupy wiekowej?”. W przeciwieństwie do testów t, które porównują średnie liczbowe, testy chi-kwadrat porównują zaobserwowane liczebności w tabeli kontyngencji z liczebnościami, których oczekiwalibyśmy, gdyby zmienne były niezależne.

Tworzenie tabeli kontyngencji za pomocą Pandas

Tabela kontyngencji (nazywana także tabelą krzyżową) przedstawia liczbę obserwacji dla każdej kombinacji dwóch zmiennych kategorialnych. pd.crosstab(df['var1'], df['var2']) tworzy tę tabelę bezpośrednio z obiektu DataFrame. Każda komórka zawiera liczbę obserwacji, w których kategoria wiersza i kategoria kolumny występują jednocześnie. Jest to dane wejściowe dla 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())

Uruchamianie chi2_contingency()

scipy.stats.chi2_contingency(observed) przyjmuje zaobserwowaną tabelę kontyngencji (jako tablicę NumPy lub obiekt Pandas DataFrame) i zwraca cztery wartości: statystykę chi-kwadrat, wartość p, liczbę stopni swobody oraz tabelę oczekiwanych liczebności. Hipoteza zerowa ma postać H₀: the two variables are independent. Jeśli p ≤ 0.05, odrzucamy niezależność i stwierdzamy, że występuje statystycznie istotna zależność.

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')

Zrozumienie oczekiwanych liczebności

Oczekiwane liczebności przedstawiają to, jak wyglądałyby liczebności w komórkach, gdyby obie zmienne były całkowicie niezależne. Dla każdej komórki expected = (row_total × column_total) / grand_total. Statystyka chi-kwadrat mierzy sumę kwadratów różnic między liczebnościami zaobserwowanymi i oczekiwanymi, znormalizowaną przez liczebności oczekiwane. Duża wartość chi-kwadrat oznacza silne odchylenie zaobserwowanych liczebności od niezależności, a mała oznacza, że dane są zgodne z niezależnością.

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))

Stopnie swobody w teście chi-kwadrat

Dla tabeli kontyngencji z r wierszami i c kolumnami liczba stopni swobody wynosi df = (r-1) × (c-1). Tabela 2×2 ma df=1, a tabela 3×4 ma df=6. Wartość krytyczna rozkładu chi-kwadrat rośnie wraz z liczbą stopni swobody: dla df=1 przy α=0.05 wartość krytyczna wynosi około 3.84, a dla df=6 przy α=0.05 około 12.6. Funkcja chi2_contingency obsługuje to automatycznie, ale znajomość wzoru pomaga zrozumieć, dlaczego w przypadku większych tabel uzyskanie istotności wymaga większych wartości statystyki chi-kwadrat.

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}')

Założenie minimalnej oczekiwanej liczebności

Test chi-kwadrat jest wiarygodny tylko wtedy, gdy wszystkie oczekiwane liczebności wynoszą co najmniej 5 (niektóre źródła dopuszczają co najmniej 1, o ile nie więcej niż 20% wartości jest mniejszych od 5). Małe oczekiwane liczebności sprawiają, że przybliżenie chi-kwadrat jest niedokładne. Gdy to założenie nie jest spełnione, w przypadku tabel 2×2 należy użyć dokładnego testu Fishera (scipy.stats.fisher_exact()) albo połączyć rzadkie kategorie, aby zwiększyć oczekiwane liczebności. Zawsze należy sprawdzić macierz oczekiwanych liczebności zwróconą przez 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}')

Dokładny test Fishera dla małych prób

scipy.stats.fisher_exact(table) oblicza dokładne prawdopodobieństwo uzyskania danej tabeli 2×2 (lub tabeli o jeszcze bardziej ekstremalnych wartościach) przy założeniu hipotezy zerowej o niezależności, bez stosowania przybliżeń. Test ten jest zawsze poprawny, niezależnie od liczebności próby czy liczebności w komórkach — wartość p jest dokładna, a nie przybliżona. Wadą jest koszt obliczeniowy: funkcja wylicza wszystkie możliwe tabele, przez co działa wolno przy dużych sumach. W przypadku tabel 2×2, w których dowolna liczebność w komórce jest mniejsza od 5, zawsze należy wybrać dokładny test Fishera zamiast testu chi-kwadrat.

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')

Pomiar siły zależności: V Craméra

Podobnie jak d Cohena w przypadku testów t, sama statystyka chi-kwadrat nie mierzy siły zależności — jej wartość rośnie wraz z liczebnością próby. V Craméra normalizuje statystykę chi-kwadrat do skali od 0 do 1: 0 oznacza brak zależności, a 1 — doskonałą zależność. V = sqrt(chi2 / (n × min(r-1, c-1))). Wytyczne: < 0.1 oznacza słabą zależność, 0.1–0.3 umiarkowaną, a > 0.3 silną. Aby uzyskać pełny obraz, zawsze należy podawać V Craméra razem z wartością p.

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')

Test chi-kwadrat zgodności

Innym zastosowaniem chi-kwadratu jest test zgodności: sprawdza on, czy zaobserwowane liczebności odpowiadają zakładanemu rozkładowi. Na przykład: „Czy wyniki rzutów kostką mają rozkład jednostajny?” albo „Czy ruch na naszej stronie internetowej odpowiada oczekiwanemu rozkładowi według dni tygodnia?”. Proszę użyć scipy.stats.chisquare(f_obs, f_exp), gdzie f_exp oznacza oczekiwane liczebności. Hipoteza zerowa mówi, że dane są zgodne ze wskazanym rozkładem.

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')

Używanie pd.crosstab z normalizacją

Podczas prezentowania wyników testu chi-kwadrat warto pokazywać proporcje zamiast surowych liczebności, aby czytelnicy mogli zobaczyć względną zależność. pd.crosstab(var1, var2, normalize='index') pokazuje proporcje wierszy (jaka część każdej kategorii wiersza przypada na poszczególne kolumny). normalize='columns' pokazuje proporcje kolumn. Wizualne porównanie tych proporcji przed wykonaniem testu pomaga przewidzieć kierunek zależności i zinterpretować wynik w odpowiednim kontekście.

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))

Test chi-kwadrat w testach A/B

Test chi-kwadrat jest standardowym testem dla współczynników konwersji w testach A/B. Należy utworzyć tabelę kontyngencji 2×2, w której wiersze oznaczają (grupę kontrolną, wariant), a kolumny (konwersję, brak konwersji). Test chi-kwadrat informuje, czy współczynnik konwersji różni się istotnie między grupami. W przypadku dużych prób jest to równoważne testowi z dla dwóch proporcji. Dla małych prób (gdy dowolna oczekiwana liczebność jest < 5) należy zamiast niego użyć dokładnego testu Fishera.

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')

Szybkie sprawdzenie

Sprawdź swoją wiedzę na temat koncepcji analizy danych omówionych w tej lekcji.

Podsumowanie lekcji

W tej lekcji poznał(a) Pan/Pani: pd.crosstab() + chi2_contingency() służą do sprawdzania, czy dwie zmienne kategorialne są niezależne, na podstawie porównania liczebności zaobserwowanych i oczekiwanych; dokładny test Fishera jest bezpieczną alternatywą, gdy oczekiwane liczebności są mniejsze od 5; a V Craméra mierzy siłę zależności niezależnie od liczebności próby. W następnej części porównamy średnie w trzech lub większej liczbie grup za pomocą ANOVA i testów post hoc.

Bezpłatny start

Ucz się Python dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
30
Lekcje
120

Często zadawane pytania

Czy lekcja „Test chi-kwadrat niezależności” jest bezpłatna?

Tak — pełny tekst „Test chi-kwadrat niezależności” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Pandas & NumPy Academy, przejdź na CoddyKit PRO. Kurs Pandas & NumPy Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Test chi-kwadrat niezależności”?

Sprawdź, czy dwie zmienne kategorialne są niezależne, używając chi2_contingency na tabeli częstości utworzonej za pomocą crosstab. Ćwiczysz Pandas & NumPy Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Pandas & NumPy Academy?

Nie wymagamy żadnego doświadczenia. Pandas & NumPy Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Test chi-kwadrat niezależności”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Pandas & NumPy Academy?

Tak. Każda lekcja Pandas & NumPy Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Statystyki opisowe i testowanie normalności
  2. Testy t do porównywania średnich
  3. Test chi-kwadrat niezależności
  4. ANOVA i testy post-hoc
← Powrót do Pandas & NumPy Academy