Implementing t-Tests in Python with SciPy — One-Sample, Two-Sample (Student's·Welch's), Paired
The theory is in the previous post — this one turns it into SciPy code for all three t-tests. Checking that the hand-computed t and p match SciPy's output makes the concepts stick.
1. Python statistics libraries
The two main statistics libraries in the Python ecosystem are SciPy and Statsmodels. (For machine learning, it's scikit-learn.)
SciPy
- Optimized for quickly producing p-values and basic statistics.
- Good for large datasets or fast decisions.
- Install:
pip install scipy
Statsmodels
- Provides extensive features for statistical model diagnosis and evaluation.
- Detailed statistics for each variable (t-statistic), the model (F-statistic), R², and more.
- Install:
pip install statsmodels
This post implements all three t-tests with SciPy.
2. One-sample t-test — stats.ttest_1samp
Example — Does the blood pressure of 5 patients after taking a drug differ from the usual mean (120)?
| Patient | Blood pressure after dose (mmHg) |
|---|---|
| P1 | 118 |
| P2 | 121 |
| P3 | 119 |
| P4 | 117 |
| P5 | 120 |
from scipy import stats
import numpy as np
x1 = np.array([118, 121, 119, 117, 120])
mu0 = 120.0 # population mean to compare against
result = stats.ttest_1samp(x1, popmean=mu0, alternative='two-sided', axis=0)
# TtestResult(statistic=-1.4142135623730951, pvalue=0.23019964108049873, df=4)
Key arguments
x1: the sample to test (numpy array recommended)popmean: the population mean to compare againstalternative:'two-sided'(default) /'less'/'greater'axis:0(columns, default) /1(rows) /None(flatten)
axis option on a 2D array
arr = np.array([[1.5, 2.3, 3.7],
[4.1, 5.2, 6.8],
[7.4, 8.5, 9.1],
[10.2, 11.4, 12.9],
[13.3, 14.1, 15.6]])
mu0 = 5
# axis=0 — column-wise (each column as a sample)
t_stat, p_val = stats.ttest_1samp(arr, popmean=mu0, axis=0)
# T-statistic: [1.0946 1.5652 2.1805]
# P-value: [0.3352 0.1926 0.0947]
# axis=1 — row-wise (each row as a sample)
t_stat, p_val = stats.ttest_1samp(arr, popmean=mu0, axis=1)
# T-statistic: [-3.8886 0.4678 6.6965 8.3224 13.8451]
# P-value: [ 0.0602 0.6860 0.0216 0.0141 0.0052]
# axis=None — flatten (whole array as one sample)
t_stat, p_val = stats.ttest_1samp(arr, popmean=mu0, axis=None)
# T-statistic: 2.9475
# P-value: 0.0106
3. Two-sample t-test — stats.ttest_ind
Example — Compare blood pressure between drug and no-drug groups.
| Patient | Group | BP after dose |
|---|---|---|
| P1 | drug | 115 |
| P2 | drug | 118 |
| P3 | drug | 116 |
| P4 | no drug | 122 |
| P5 | no drug | 124 |
3.1 Student's t-test — equal-variance assumption OK
from scipy import stats
import numpy as np
x1 = np.array([115, 118, 116]) # drug
x2 = np.array([122, 124]) # no drug
t_stat, p_val = stats.ttest_ind(x1, x2, equal_var=True, alternative='two-sided')
# T-statistic: -4.898979485566359
# P-value: 0.016276603459428517
Key arguments
x1,x2: the two samplesequal_var:True: equal-variance assumption holds → Student's t-testFalse: equal-variance assumption fails → automatically switches to Welch's t-testalternativeandaxisare the same asttest_1samp
axis on 2D arrays
data = np.array([[115, 118, 116],
[122, 124, 123]])
data1 = np.array([[231, 123, 132],
[223, 321, 421]])
# axis=0 — column-wise
t_stat, p_val = stats.ttest_ind(data, data1, axis=0, equal_var=True)
# T-statistic: [-20.4136 -1.0197 -1.0862]
# P-value: [ 0.0024 0.4151 0.3909]
# axis=1 — row-wise
t_stat, p_val = stats.ttest_ind(data, data1, axis=1, equal_var=True)
# T-statistic: [-1.3195 -3.4755]
# P-value: [ 0.2575 0.0255]
3.2 Welch's t-test — unequal variances
t_stat, p_val = stats.ttest_ind(x1, x2, equal_var=False, alternative='two-sided')
# T-statistic: -5.0000
# P-value: 0.02505
Same method, just toggle equal_var=False for Welch's. Use this when the equal-variance assumption doesn't hold.
4. Paired-samples t-test — stats.ttest_rel
Example — Same patients, blood pressure before vs. after. Paired data.
| Patient | Before | After |
|---|---|---|
| P1 | 130 | 120 |
| P2 | 128 | 119 |
| P3 | 135 | 125 |
| P4 | 132 | 123 |
| P5 | 129 | 121 |
import numpy as np
from scipy import stats
before = np.array([130, 128, 135, 132, 129])
after = np.array([120, 119, 125, 123, 121])
t_stat, p_val = stats.ttest_rel(before, after, alternative='less')
# T-statistic: 24.58803425594304
# P-value: 0.9999918819424217
alternative='less'here sets "before < after" as the alternative hypothesis. Sincebeforeis actually larger, p is near 1. If you want to test "is BP lower after the dose," call it the other way:stats.ttest_rel(after, before, alternative='less')— then p comes out tiny.
Caveat: the two arrays must have the same length. If not:
ValueError: unequal length arrays
Wrap-up
With SciPy's three functions, each t-test is a one-liner:
- One-sample →
stats.ttest_1samp(x, popmean) - Two-sample →
stats.ttest_ind(x1, x2, equal_var=True/False)— Student's vs. Welch's - Paired →
stats.ttest_rel(before, after)
Just keep alternative (two-sided / one-sided) and axis (1D/2D) in mind. Compare the output against the hand calculation from the theory post — t and p match.
References
📦 Migrated from my own Korean blog (my own writing). Original: taehyuklee.tistory.com/26

Comments