0Pricing
Pandas & NumPy Academy · 강의

평균 비교를 위한 t 검정

scipy.stats로 일표본, 독립 이표본, 대응표본 t 검정을 실행하고 신뢰구간을 해석합니다.

평균 비교를 위한 t 검정은(는) CoddyKit의 무료 Pandas & NumPy Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Pandas & NumPy Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Pandas & NumPy Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.

What Is a T-Test?

A t-test is a hypothesis test that determines whether a difference in means between groups is statistically significant or likely due to random chance. It compares the observed difference to the variability in the data, producing a t-statistic and a p-value. If p ≤ 0.05 (the conventional threshold), we reject the null hypothesis that the means are equal. There are three common types: one-sample, independent two-sample, and paired t-test, each for different experimental designs.

One-Sample T-Test

The one-sample t-test tests whether the mean of a sample differs significantly from a known or hypothesised population mean. For example: 'Is our average delivery time significantly different from the industry standard of 3 days?' Use scipy.stats.ttest_1samp(data, popmean). The null hypothesis is H₀: mean = popmean. A small p-value leads you to reject H₀ and conclude the sample mean differs from the target.

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

Independent Two-Sample T-Test

The independent two-sample t-test compares the means of two independent groups. For example: 'Does the A/B test variant produce higher average revenue than the control?' Use scipy.stats.ttest_ind(group_a, group_b). The equal_var parameter matters: set equal_var=False (Welch's t-test) when the two groups may have different variances, which is the safe default for most real data.

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

One-Tailed vs Two-Tailed Tests

By default, t-tests are two-tailed: they test whether means differ in either direction (greater OR less than). If you have a directional hypothesis ('the variant increases revenue'), use a one-tailed test with the alternative parameter: 'greater' or 'less'. A one-tailed test has more power for the specific direction but provides no evidence about the other direction. Decide on the direction before looking at the data to avoid HARKing (Hypothesising After Results are Known).

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

Paired T-Test

The paired t-test is used when each observation in group A has a natural partner in group B — for example, measurements before and after a treatment on the same subjects, or the same stores in two different months. Pairing controls for between-subject variability, making the test more powerful than an independent t-test for the same data. Use scipy.stats.ttest_rel(before, after). The order of elements must be matched: before[i] and after[i] must come from the same subject.

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

Interpreting the T-Statistic

The t-statistic measures how many standard errors the observed difference is from zero. A t-statistic of 2 means the observed difference is 2 standard errors above what would be expected under the null. For a two-tailed test at α=0.05 with a large sample, the critical value is approximately ±1.96 — so |t| > 1.96 gives p < 0.05. The p-value converts the t-statistic to a probability, making it easier to interpret without consulting t-distribution tables.

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

Confidence Intervals for the Mean Difference

A p-value alone does not tell you the magnitude of the effect. Always compute a confidence interval for the mean difference. A 95% CI that includes zero is consistent with p > 0.05 (not significant); one that excludes zero means the difference is significant. The CI also tells you whether the effect size is practically meaningful — a statistically significant difference of $0.01 in revenue may be irrelevant, while a $50 difference with p=0.06 may still matter for the business.

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)

Effect Size: Cohen's d

Statistical significance depends on sample size — with large enough samples, even trivial differences become significant. Cohen's d measures practical significance (effect size): the mean difference divided by the pooled standard deviation. Guidelines: d < 0.2 is negligible, 0.2–0.5 is small, 0.5–0.8 is medium, > 0.8 is large. Always report effect size alongside p-values — a significant p-value with d = 0.05 means the difference is real but probably not meaningful in practice.

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

Assumptions of the T-Test

The independent t-test assumes: (1) Independence — observations within each group are independent of each other; (2) Normality — data in each group is approximately normal (robust for n > 30 via CLT); (3) for Student's t-test (equal_var=True), equal variances. Welch's t-test (equal_var=False) relaxes assumption 3 and is preferred. When normality is seriously violated with small samples (< 30), use the Mann-Whitney U test instead.

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

T-Tests on Pandas Series

In practice, the data you want to test lives in a Pandas DataFrame column. You can pass a Pandas Series directly to scipy.stats functions — they work seamlessly with Series as well as NumPy arrays. A common workflow: filter the DataFrame to get each group's Series, run the t-test, and store results in a summary DataFrame. This pattern scales to testing many pairs of columns or groups in a loop.

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

Multiple Comparisons Problem

Running many t-tests simultaneously inflates the chance of a false positive. If you run 20 t-tests at α=0.05, you expect 1 false positive by chance. This is the multiple comparisons problem. Apply the Bonferroni correction: divide α by the number of tests (p_threshold = 0.05 / n_tests). For more power with less conservatism, use the Benjamini-Hochberg False Discovery Rate (FDR) correction available in statsmodels. Always correct for multiple comparisons in A/B testing with many metrics.

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

Quick Check

Test your understanding of Data Analysis concepts from this lesson.

Lesson Recap

In this lesson you learned: ttest_1samp tests a sample mean against a reference value, ttest_ind (Welch's with equal_var=False) compares two independent groups, and ttest_rel handles paired measurements. Always report Cohen's d alongside the p-value to distinguish statistical from practical significance. Next up we test relationships between categorical variables with the chi-squared test.

자주 묻는 질문

“평균 비교를 위한 t 검정” 강의는 무료인가요?

네 — “평균 비교를 위한 t 검정” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Pandas & NumPy Academy 강의 전체를 잠금 해제할 수 있습니다. Pandas & NumPy Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“평균 비교를 위한 t 검정”에서 뭘 배우나요?

scipy.stats로 일표본, 독립 이표본, 대응표본 t 검정을 실행하고 신뢰구간을 해석합니다. 브라우저에서 직접 실행하는 실습 코드로 Pandas & NumPy Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Pandas & NumPy Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Pandas & NumPy Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“평균 비교를 위한 t 검정” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Pandas & NumPy Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Pandas & NumPy Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 기술 통계와 정규성 검정
  2. 평균 비교를 위한 t 검정
  3. 독립성에 대한 카이제곱 검정
  4. ANOVA와 사후 검정
← Pandas & NumPy Academy(으)로 돌아가기