Pandas & NumPy Academy · Lekcja

Testy t do porównywania średnich

Wykonaj testy t dla jednej próby, dwóch niezależnych prób i prób zależnych za pomocą scipy.stats oraz zinterpretuj przedziały ufności.

Lekcja 2 z 413 kroki

Testy t do porównywania średnich to bezpłatna lekcja Pandas & NumPy Academy na CoddyKit. To lekcja 2 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.

Czym jest test t?

Test t jest testem hipotezy, który określa, czy różnica średnich między grupami jest istotna statystycznie, czy też prawdopodobnie wynika z losowego przypadku. Porównuje zaobserwowaną różnicę ze zmiennością danych, zwracając statystykę t i wartość p. Jeśli p ≤ 0.05 (konwencjonalny próg), odrzucamy hipotezę zerową mówiącą, że średnie są równe. Wyróżnia się trzy popularne typy: test t dla jednej próbki, test t dla dwóch prób niezależnych oraz test t dla prób zależnych; każdy z nich służy do innego rodzaju planu eksperymentu.

Test t dla jednej próbki

Test t dla jednej próbki sprawdza, czy średnia z próbki różni się istotnie od znanej lub hipotetycznej średniej populacji. Przykład: „Czy nasz średni czas dostawy różni się istotnie od branżowego standardu wynoszącego 3 dni?”. Użyj scipy.stats.ttest_1samp(data, popmean). Hipoteza zerowa ma postać H₀: mean = popmean. Mała wartość p prowadzi do odrzucenia H₀ i wniosku, że średnia z próbki różni się od wartości docelowej.

import numpy as np
from scipy import stats

np.random.seed(42)
# Delivery times in days (true mean is ~3.5, not 3)
delivery_times = np.random.normal(loc=3.5, scale=0.8, size=50)

# Test: is our mean significantly different from 3 days?
stat, p = stats.ttest_1samp(delivery_times, popmean=3.0)
print(f'Sample mean: {delivery_times.mean():.3f}')
print(f't-statistic: {stat:.3f}')
print(f'p-value: {p:.4f}')
print('Differs from 3?', 'Yes' if p < 0.05 else 'No')

Test t dla dwóch prób niezależnych

Test t dla dwóch niezależnych prób porównuje średnie dwóch niezależnych grup. Na przykład: „Czy wariant testu A/B generuje wyższy średni przychód niż grupa kontrolna?”. Proszę użyć scipy.stats.ttest_ind(group_a, group_b). Parametr equal_var ma znaczenie: proszę ustawić equal_var=False (test t Welcha), gdy obie grupy mogą mieć różne wariancje — to bezpieślna wartość domyślna dla większości rzeczywistych danych.

import numpy as np
from scipy import stats

np.random.seed(0)
# A/B test revenue per session
control = np.random.normal(loc=25.0, scale=8.0, size=80)
variant = np.random.normal(loc=28.0, scale=9.0, size=80)

# Welch's t-test (does not assume equal variances)
stat, p = stats.ttest_ind(control, variant, equal_var=False)
print(f'Control mean: {control.mean():.2f}')
print(f'Variant mean: {variant.mean():.2f}')
print(f't-statistic: {stat:.3f}')
print(f'p-value: {p:.4f}')
print('Significant difference?', 'Yes' if p < 0.05 else 'No')

Testy jednostronne a dwustronne

Domyślnie testy t są dwustronne: sprawdzają, czy średnie różnią się w którymkolwiek kierunku (większa LUB mniejsza). Jeśli ma Pan/Pani hipotezę kierunkową („wariant zwiększa przychód”), proszę użyć testu jednostronnego z parametrem alternative: 'greater' lub 'less'. Test jednostronny ma większą moc dla konkretnego kierunku, ale nie dostarcza informacji o kierunku przeciwnym. Kierunek należy określić przed sprawdzeniem danych, aby uniknąć HARKingu (formułowania hipotezy po poznaniu wyników).

import numpy as np
from scipy import stats

np.random.seed(1)
control = np.random.normal(25, 8, 100)
variant = np.random.normal(27, 8, 100)

# Two-tailed (default): does mean differ at all?
stat, p_two = stats.ttest_ind(control, variant, equal_var=False,
                              alternative='two-sided')
# One-tailed: is variant > control?
stat, p_one = stats.ttest_ind(control, variant, equal_var=False,
                              alternative='less')

print(f'Two-tailed p: {p_two:.4f}')
print(f'One-tailed p (variant greater): {p_one:.4f}')

Test t dla prób zależnych

Test t dla prób zależnych stosuje się, gdy każda obserwacja w grupie A ma naturalnie odpowiadającą jej obserwację w grupie B — na przykład pomiary wykonane przed leczeniem i po nim u tych samych osób albo w tych samych sklepach w dwóch różnych miesiącach. Pary kontrolują zmienność między obiektami, dzięki czemu test ma większą moc niż test t dla prób niezależnych przy tych samych danych. Proszę użyć scipy.stats.ttest_rel(before, after). Kolejność elementów musi być zgodna: before[i] i after[i] muszą pochodzić od tej samej osoby.

import numpy as np
from scipy import stats

np.random.seed(5)
# Blood pressure before and after medication (paired)
before = np.random.normal(130, 10, 30)
after = before - np.random.normal(5, 3, 30)  # Treatment reduces BP by ~5

stat, p = stats.ttest_rel(before, after)
print(f'Mean before: {before.mean():.2f}')
print(f'Mean after: {after.mean():.2f}')
print(f't-statistic: {stat:.3f}')
print(f'p-value: {p:.4f}')
print('Treatment effective?', 'Yes' if p < 0.05 else 'No')

Interpretacja statystyki t

Statystyka t mierzy, ile błędów standardowych dzieli zaobserwowaną różnicę od zera. Statystyka t równa 2 oznacza, że zaobserwowana różnica jest o 2 błędy standardowe większa od wartości oczekiwanej przy założeniu hipotezy zerowej. Dla testu dwustronnego przy α=0.05 i dużej próbie wartość krytyczna wynosi w przybliżeniu ±1.96, więc |t| > 1.96 oznacza p < 0.05. Wartość p przekształca statystykę t w prawdopodobieństwo, dzięki czemu łatwiej ją interpretować bez korzystania z tablic rozkładu t.

import numpy as np
from scipy import stats

# Demonstrate relationship between t-statistic and p-value
for t_val in [1.0, 1.96, 2.5, 3.0, 4.0]:
    # degrees of freedom = large sample -> t ~ normal
    p = 2 * stats.t.sf(abs(t_val), df=100)  # two-tailed
    print(f't = {t_val:4.2f} -> p = {p:.4f} '
          f'({'significant' if p < 0.05 else 'not significant'})')

Przedziały ufności dla różnicy średnich

Sama wartość p nie mówi o wielkości efektu. Zawsze należy obliczyć przedział ufności dla różnicy średnich. 95-procentowy PU obejmujący zero jest zgodny z p > 0.05 (wynik nieistotny); przedział niezawierający zera oznacza, że różnica jest istotna. PU informuje również, czy wielkość efektu ma praktyczne znaczenie — statystycznie istotna różnica 0,01 USD w przychodzie może być nieistotna z praktycznego punktu widzenia, podczas gdy różnica 50 USD przy p=0.06 może nadal mieć znaczenie dla firmy.

import numpy as np
from scipy import stats

np.random.seed(0)
control = np.random.normal(25, 8, 80)
variant = np.random.normal(28, 8, 80)

diff = variant.mean() - control.mean()
se = np.sqrt(control.var()/len(control) + variant.var()/len(variant))
df = len(control) + len(variant) - 2
t_crit = stats.t.ppf(0.975, df=df)
ci_low = diff - t_crit * se
ci_high = diff + t_crit * se

print(f'Mean difference: {diff:.2f}')
print(f'95% CI: [{ci_low:.2f}, {ci_high:.2f}]')
print('CI excludes zero:', ci_low > 0 or ci_high < 0)

Wielkość efektu: d Cohena

Istotność statystyczna zależy od liczebności próby — przy dostatecznie dużych próbach istotne stają się nawet błahe różnice. d Cohena mierzy istotność praktyczną (wielkość efektu): jest to różnica średnich podzielona przez odchylenie standardowe łączone. Wytyczne: d < 0.2 oznacza efekt pomijalny, 0.2–0.5 mały, 0.5–0.8 średni, a > 0.8 duży. Zawsze należy podawać wielkość efektu razem z wartościami p — istotna wartość p przy d = 0.05 oznacza, że różnica jest rzeczywista, ale prawdopodobnie nie ma praktycznego znaczenia.

import numpy as np
from scipy import stats

np.random.seed(0)
g1 = np.random.normal(25, 8, 200)
g2 = np.random.normal(28, 8, 200)

stat, p = stats.ttest_ind(g1, g2)

# Cohen's d
pooled_std = np.sqrt((g1.var() + g2.var()) / 2)
cohens_d = (g2.mean() - g1.mean()) / pooled_std

print(f'p-value: {p:.4f}')
print(f'Cohen\'s d: {cohens_d:.3f}')

if cohens_d < 0.2: print('Effect: negligible')
elif cohens_d < 0.5: print('Effect: small')
elif cohens_d < 0.8: print('Effect: medium')
else: print('Effect: large')

Założenia testu t

Test t dla prób niezależnych zakłada: (1) niezależność — obserwacje w obrębie każdej grupy są od siebie niezależne; (2) normalność — dane w każdej grupie mają w przybliżeniu rozkład normalny (dzięki centralnemu twierdzeniu granicznemu test jest odporny dla n > 30); (3) w przypadku testu t Studenta (equal_var=True) równość wariancji. Test t Welcha (equal_var=False) nie wymaga założenia 3 i jest preferowany. Gdy przy małych próbach (< 30) założenie normalności jest poważnie naruszone, należy zamiast niego użyć testu U Manna-Whitneya.

import numpy as np
from scipy import stats

np.random.seed(0)
g1 = np.random.normal(10, 2, 30)
g2 = np.random.normal(11, 5, 30)  # Very different variance!

# Levene test for equal variances
stat_l, p_l = stats.levene(g1, g2)
print(f'Levene test p: {p_l:.4f}')
if p_l < 0.05:
    print('Unequal variances -> use Welch\'s (equal_var=False)')
    stat, p = stats.ttest_ind(g1, g2, equal_var=False)
else:
    print('Equal variances OK -> Student\'s t-test')
    stat, p = stats.ttest_ind(g1, g2, equal_var=True)
print(f'Result: t={stat:.3f}, p={p:.4f}')

Testy t na obiektach Pandas Series

W praktyce dane, które chce Pan/Pani testować, znajdują się w kolumnie obiektu Pandas DataFrame. Obiekt Pandas Series można bezpośrednio przekazać do funkcji z scipy.stats — działają one bezproblemowo zarówno z obiektami Series, jak i z tablicami NumPy. Typowy przebieg pracy wygląda następująco: filtrowanie DataFrame w celu uzyskania obiektu Series dla każdej grupy, wykonanie testu t i zapisanie wyników w podsumowującym DataFrame. Ten schemat można stosować do testowania wielu par kolumn lub grup w pętli.

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

np.random.seed(0)
df = pd.DataFrame({
    'group': ['A']*60 + ['B']*60,
    'revenue': np.concatenate([
        np.random.normal(100, 20, 60),
        np.random.normal(110, 22, 60)
    ])
})

grp_a = df.loc[df['group'] == 'A', 'revenue']
grp_b = df.loc[df['group'] == 'B', 'revenue']

stat, p = stats.ttest_ind(grp_a, grp_b, equal_var=False)
print(f'A mean: {grp_a.mean():.2f}, B mean: {grp_b.mean():.2f}')
print(f'p-value: {p:.4f}')

Problem wielokrotnych porównań

Jednoczesne wykonywanie wielu testów t zwiększa prawdopodobieństwo wyniku fałszywie dodatniego. Jeśli wykona Pan/Pani 20 testów t przy α=0.05, należy oczekiwać 1 fałszywie dodatniego wyniku wynikającego z przypadku. Jest to problem wielokrotnych porównań. Proszę zastosować poprawkę Bonferroniego: podzielić α przez liczbę testów (p_threshold = 0.05 / n_tests). Aby uzyskać większą moc przy mniejszej konserwatywności, proszę użyć poprawki Benjamini-Hochberga kontrolującej odsetek fałszywych odkryć (FDR), dostępnej w statsmodels. W testach A/B obejmujących wiele metryk zawsze należy korygować wynik pod kątem wielokrotnych porównań.

import numpy as np
from scipy import stats

np.random.seed(0)
n_tests = 10
results = []
for i in range(n_tests):
    g1 = np.random.normal(0, 1, 50)
    g2 = np.random.normal(0.1, 1, 50)  # Tiny true effect
    _, p = stats.ttest_ind(g1, g2)
    results.append(p)

print('p-values:', [round(p, 3) for p in results])
bonferroni_thresh = 0.05 / n_tests
print(f'Bonferroni threshold: {bonferroni_thresh}')
print('Significant after Bonferroni:', sum(p < bonferroni_thresh for p in results))

Szybkie sprawdzenie

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

Podsumowanie lekcji

W tej lekcji poznał(a) Pan/Pani: ttest_1samp służy do testowania średniej z próby względem wartości referencyjnej, ttest_ind (test t Welcha z equal_var=False) porównuje dwie niezależne grupy, a ttest_rel obsługuje pomiary zależne. Zawsze należy podawać d Cohena razem z wartością p, aby odróżnić istotność statystyczną od praktycznej. W następnej części sprawdzimy zależności między zmiennymi kategorialnymi za pomocą testu chi-kwadrat.

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 „Testy t do porównywania średnich” jest bezpłatna?

Tak — pełny tekst „Testy t do porównywania średnich” 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 „Testy t do porównywania średnich”?

Wykonaj testy t dla jednej próby, dwóch niezależnych prób i prób zależnych za pomocą scipy.stats oraz zinterpretuj przedziały ufności. Ć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 2 z 4.

Ile czasu zajmuje lekcja „Testy t do porównywania średnich”?

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