Back to List

Statistical Analysis of Experiments with Python

Perform t-tests and ANOVA with scipy and learn to interpret p-values. A statistics introduction for bio researchers.

Intermediate
|
90min
|
Verified (2026-06)
p-valuet-testANOVAHypothesis TestingscipyStatistical Significance
Progress0/12 (0%)

Statistical Analysis of Experiments with Python

After Completing This Topic

You'll be able to test differences between two groups with a t-test, compare three or more groups with ANOVA, and correctly interpret p-values in Python.


Why Statistical Testing Matters

A drug-treated group has a mean cell viability of 80%, while the control is 85%. Can you conclude "the drug has an effect"?

Different means don't necessarily mean "there is a difference." Repeating an experiment yields different values each time โ€” this is variation. You need to distinguish whether a 5% difference is a drug effect or just experimental error.

The tool for this judgment is a statistical test, and its result is the p-value.

What Is a p-value?

A p-value is "the probability that a difference this large would occur by chance, assuming there is no real difference."

text
p-value = 0.03 โ†’ "If there's no difference, this result happens by chance 3% of the time"
                โ†’ Hard to call it chance โ†’ Conclude there IS a difference

p-value = 0.42 โ†’ "If there's no difference, this result happens by chance 42% of the time"
                โ†’ Could easily be chance โ†’ Can't conclude there's a difference

By convention, p < 0.05 is called "statistically significant." However, 0.05 is a convention, not an absolute standard.

t-test: Comparing Two Groups

Independent t-test โ€” compares the means of two independent groups.

python
import numpy as np
from scipy import stats
# Drug-treated group cell viability (%)
drug = np.array([78, 82, 75, 80, 77, 83, 79, 76])
# Control group cell viability (%)
control = np.array([85, 88, 82, 86, 90, 84, 87, 89])
t_stat, p_value = stats.ttest_ind(drug, control)
print(f"Drug mean: {drug.mean():.1f}%")
print(f"Control mean: {control.mean():.1f}%")
print(f"t-statistic: {t_stat:.3f}")
print(f"p-value: {p_value:.4f}")
if p_value < 0.05:
print("โ†’ Statistically significant difference (p < 0.05)")
else:
print("โ†’ No significant difference")
assert p_value < 0.05

stats.ttest_ind() โ€” independent t-test. Used when the two groups are from different samples.

Paired t-test: Before/After on the Same Samples

When comparing pre/post treatment in the same patients, use a paired t-test:

python
from scipy import stats
import numpy as np
before = np.array([120, 135, 128, 142, 138, 125])
after = np.array([115, 128, 122, 130, 132, 118])
t_stat, p_value = stats.ttest_rel(before, after)
print(f"Pre-treatment mean: {before.mean():.1f}")
print(f"Post-treatment mean: {after.mean():.1f}")
print(f"p-value: {p_value:.4f}")

ttest_rel() โ€” related (paired) t-test. Compares measurements from the same subject.

SituationTest Methodscipy Function
Two different groupsIndependent t-teststats.ttest_ind()
Same subject, before/afterPaired t-teststats.ttest_rel()

ANOVA: Comparing Three or More Groups

What if you want to compare cell proliferation across three media (DMEM, RPMI, MEM)? Since t-tests only compare two groups, you use ANOVA (Analysis of Variance).

python
from scipy import stats
import numpy as np
dmem = np.array([1.2, 1.4, 1.3, 1.5, 1.1])
rpmi = np.array([1.8, 1.7, 1.9, 2.0, 1.6])
mem = np.array([1.0, 1.1, 0.9, 1.2, 1.0])
f_stat, p_value = stats.f_oneway(dmem, rpmi, mem)
print(f"DMEM mean: {dmem.mean():.2f}")
print(f"RPMI mean: {rpmi.mean():.2f}")
print(f"MEM mean: {mem.mean():.2f}")
print(f"F-statistic: {f_stat:.3f}")
print(f"p-value: {p_value:.6f}")
assert p_value < 0.05

A significant ANOVA p-value means "at least one of the three groups is different." To find which group differs, use a post-hoc test.

Visualizing Results: Box Plots

Numbers alone make it hard to see distributions. Visualize with box plots:

python
import matplotlib.pyplot as plt
import numpy as np
drug = np.array([78, 82, 75, 80, 77, 83, 79, 76])
control = np.array([85, 88, 82, 86, 90, 84, 87, 89])
fig, ax = plt.subplots(figsize=(6, 4))
ax.boxplot([drug, control], labels=["Drug", "Control"])
ax.set_ylabel("Cell Viability (%)")
ax.set_title("Drug vs Control")
plt.tight_layout()
plt.savefig("drug_vs_control.png", dpi=150)
plt.show()

Box plots show the median, interquartile range, and outliers at a glance. They're one of the most common graph types in paper figures.

Caveats: Pitfalls of p-values

Points to note when interpreting p-values:

1. p < 0.05 doesn't mean an "important" difference

Statistical significance and practical significance are different. With very large sample sizes, even tiny differences can yield p < 0.05. Always ask: "Is this difference biologically meaningful?"

2. p > 0.05 doesn't mean "no difference"

It means "we didn't find evidence of a difference." With too few samples, real differences may go undetected.

3. Multiple comparisons problem

Testing 20,000 genes at once means 5% โ€” that's 1,000 genes โ€” will be p < 0.05 by chance. In this case, Bonferroni correction or FDR (False Discovery Rate) correction is needed.

Try It Yourself (Faded Example)

Fill in the blanks to perform a t-test between two groups.

Fill in the Blankspython
from import stats
import numpy as np
treated = np.array([4.2, 3.8, 4.5, 4.1, 3.9])
control = np.array([5.1, 5.3, 4.9, 5.0, 5.2])
t_stat, p_value = stats.ind(treated, control)
print(f"p-value: {p_value:.4f}")
if p_value < :
print("Significant difference")

Common Errors & Solutions

Q: ModuleNotFoundError: No module named 'scipy'

Install with pip install scipy. It's already installed on Google Colab.

Q: Should I use a t-test or ANOVA?

Use t-test for 2 groups, ANOVA for 3 or more. Running t-tests 3 times on 3 groups (A-B, A-C, B-C) creates a multiple comparisons problem, so use ANOVA instead.

Q: What if my data isn't normally distributed?

t-tests and ANOVA assume data roughly follows a normal distribution. For non-normal data, use non-parametric tests: Mann-Whitney U test (stats.mannwhitneyu()), Kruskal-Wallis test (stats.kruskal()).

Q: nan appears in results

Your data may contain missing values (NaN). Use nan-ignoring functions like np.nanmean(data), or remove missing values with df.dropna() in Pandas before testing.

๐Ÿ’ฌ Questions & Comments

0 comments

You can post without signing in. Guest comments cannot be edited or deleted by their author.

0/2000

Loading...