Foundations biostatistics fundamentals
Skill Pavel-Kravchenko/Bioinformatics/Skills/foundations-biostatistics-fundamentals
Run t-test/Mann-Whitney/ANOVA/chi-square tests, BH-FDR correction, and power analysis in SciPy, statsmodels, R. Use when comparing groups, interpreting p-values/CIs, correcting many gene-level tests, or sizing an experiment.From its SKILL.md
npx -y skills add Pavel-Kravchenko/Bioinformatics --skill foundations-biostatistics-fundamentalsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
8.5 KB, ~2.3k tokens by cl100k_base, as published. Nobody here has run it
Biostatistics Fundamentals
When to Use
- Deciding whether an observed difference (expression, protein level, allele frequency) between two or more groups is likely real or noise.
- Choosing between a parametric test (t-test, ANOVA, Pearson) and its non-parametric counterpart (Mann-Whitney, Kruskal-Wallis, Spearman).
- Correcting p-values across thousands of simultaneous tests (genes, variants, metabolites) with Bonferroni or Benjamini-Hochberg FDR.
- Computing a confidence interval around a mean/proportion, or estimating the sample size needed to detect an effect (power analysis).
- Sanity-checking a downstream tool's output (DESeq2, edgeR, GWAS) by understanding what the p-value and effect size actually mean.
Version Compatibility
Python ≥3.10, NumPy ≥1.24, SciPy ≥1.11, statsmodels ≥0.14, matplotlib ≥3.7. R ≥4.3 with base stats (no extra packages needed for the tests shown here).
Prerequisites
pip install numpy scipy statsmodels matplotlib- Comfortable with arrays/DataFrames; no prior stats background assumed, but you should know your experimental design (paired vs. independent groups, number of comparisons).
Descriptive Statistics and Confidence Intervals
Goal: Summarize a sample's center/spread and quantify uncertainty in the estimated mean. Approach: Report the median/IQR alongside the mean/SD (robust to outliers), and use the SEM with a t-distribution critical value for the confidence interval — not the normal distribution, unless n is large.
import numpy as np
from scipy import stats
def descriptive_summary(x: np.ndarray, confidence: float = 0.95) -> dict:
"""Return center, spread, and a confidence interval for the mean of x.
Uses the t-distribution (not z) since the population SD is unknown,
which is almost always true for biological samples.
"""
x = np.asarray(x, dtype=float)
n = len(x)
mean, sd = np.mean(x), np.std(x, ddof=1)
sem = sd / np.sqrt(n)
q25, q75 = np.percentile(x, [25, 75])
t_crit = stats.t.ppf((1 + confidence) / 2, df=n - 1)
ci = (mean - t_crit * sem, mean + t_crit * sem)
return {
"n": n, "mean": mean, "median": np.median(x), "sd": sd,
"sem": sem, "iqr": q75 - q25, "ci": ci,
}
expression = np.append(np.random.default_rng(42).normal(10, 2, 100), [20, 22, 25]) # outliers
print(descriptive_summary(expression))
Choosing and Running a Hypothesis Test
Goal: Test whether two (or more) groups differ, using the test that matches the study design and data shape. Approach: Check normality visually (Q-Q plot) plus Shapiro-Wilk as a rough guide (unreliable at very large/small n), match paired vs. independent design, then pick parametric or rank-based.
import numpy as np
from scipy import stats
def compare_two_groups(a: np.ndarray, b: np.ndarray, paired: bool = False, alpha: float = 0.05) -> dict:
"""Compare two groups with the appropriate t-test or its non-parametric counterpart.
Runs Shapiro-Wilk on both groups; if either rejects normality (p < alpha),
falls back to Mann-Whitney U (independent) or Wilcoxon signed-rank (paired).
"""
a, b = np.asarray(a, float), np.asarray(b, float)
normal_a = stats.shapiro(a).pvalue > alpha
normal_b = stats.shapiro(b).pvalue > alpha
if normal_a and normal_b:
if paired:
stat, p = stats.ttest_rel(a, b)
test = "paired t-test"
else:
stat, p = stats.ttest_ind(a, b)
test = "independent t-test"
else:
if paired:
stat, p = stats.wilcoxon(a, b)
test = "Wilcoxon signed-rank"
else:
stat, p = stats.mannwhitneyu(a, b, alternative="two-sided")
test = "Mann-Whitney U"
pooled_sd = np.sqrt((np.var(a, ddof=1) + np.var(b, ddof=1)) / 2)
cohens_d = (np.mean(a) - np.mean(b)) / pooled_sd
return {"test": test, "statistic": stat, "p_value": p, "cohens_d": cohens_d}
rng = np.random.default_rng(42)
normal_tissue = rng.normal(10, 2, 30)
tumor_tissue = rng.normal(12, 2.5, 30)
print(compare_two_groups(normal_tissue, tumor_tissue))
For 3+ groups use stats.f_oneway (ANOVA, parametric) or stats.kruskal (Kruskal-Wallis, non-parametric). For categorical 2x2 tables use stats.chi2_contingency (large n) or stats.fisher_exact (small n, gives an odds ratio).
Multiple Testing Correction and Power Analysis
Goal: Control the false discovery rate when testing thousands of genes/variants at once, and determine the sample size needed before running the experiment. Approach: Never read raw p-values genome-wide — always correct with BH/FDR (less conservative than Bonferroni, standard for genomics). Before collecting data, use power analysis to justify replicate counts.
import numpy as np
from statsmodels.stats.multitest import multipletests
from statsmodels.stats.power import TTestIndPower
def correct_pvalues(pvalues: np.ndarray, alpha: float = 0.05, method: str = "fdr_bh") -> dict:
"""Adjust p-values for multiple comparisons and report how many pass."""
reject, adj_p, _, _ = multipletests(pvalues, alpha=alpha, method=method)
return {"n_significant": int(reject.sum()), "adjusted_pvalues": adj_p, "reject": reject}
def samples_needed(effect_size: float, power: float = 0.80, alpha: float = 0.05) -> int:
"""Minimum samples per group (two-sided independent t-test) for the given power."""
n = TTestIndPower().solve_power(effect_size=effect_size, alpha=alpha, power=power, alternative="two-sided")
return int(np.ceil(n))
rng = np.random.default_rng(42)
pvals = np.concatenate([rng.uniform(0, 1, 9800), rng.beta(0.3, 10, 200)]) # 200 truly DE genes
result = correct_pvalues(pvals)
print(f"Significant after BH-FDR: {result['n_significant']} / {len(pvals)}")
print(f"n per group for medium effect (d=0.5), 80% power: {samples_needed(0.5)}")
Equivalent workflow in R (base stats, no extra packages):
## Two-group comparison with normality check, then p-value correction
group_a <- rnorm(30, mean = 10, sd = 2)
group_b <- rnorm(30, mean = 12, sd = 2.5)
shapiro.test(group_a)$p.value ## if < 0.05, prefer wilcox.test below
shapiro.test(group_b)$p.value
t_result <- t.test(group_a, group_b) ## Welch's t-test by default
wilcox_result <- wilcox.test(group_a, group_b)
cat("t-test p =", t_result$p.value, "\n")
cat("Mann-Whitney p =", wilcox_result$p.value, "\n")
## Multiple testing correction across many genes
pvals <- c(runif(9800), rbeta(200, 0.3, 10))
padj <- p.adjust(pvals, method = "BH")
cat("Significant after BH-FDR:", sum(padj < 0.05), "\n")
## Power analysis: samples per group for medium effect size (Cohen's d = 0.5)
power.t.test(delta = 0.5, sd = 1, power = 0.80, sig.level = 0.05)$n
Pitfalls
- P-value is not the probability that H0 is true: it is P(data this extreme | H0 true). Confusing these two is the most common statistics error in papers.
- Statistical significance ≠ biological significance: with n = 10,000, a 0.1% fold-change will be "significant." Always report effect size (Cohen's d, log2FC) alongside p-values.
- Multiple testing explosion: testing 20,000 genes at alpha = 0.05 expects 1,000 false positives even with zero true effects — always apply BH/FDR correction in genomics.
- Paired vs. unpaired mismatch: running an independent t-test on before/after data from the same patients discards information and loses power; match the test to the design.
- Shapiro-Wilk is not a decision oracle: it over-rejects normality at large n and under-detects it at small n. Look at a Q-Q plot and use domain knowledge, not just the p-value.
- R's
t.testdefaults to Welch's (unequal variance), whilescipy.stats.ttest_inddefaults to equal-variance (Student's) unless you passequal_var=False— these can give different p-values on the same data.
See Also
bio-experimental-design-power-analysis— deeper sample-size and power calculations for specific study designs.bio-experimental-design-multiple-testing— FDR/Bonferroni strategies beyond the basics shown here.bio-differential-expression-de-results— applying these tests to real RNA-seq count data (DESeq2/edgeR output).bio-population-genetics-association-testing— hypothesis testing applied to genotype-phenotype association (GWAS).
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 0 of the 12 instructions most test skills give in ~2.3k tokens
Counted across 1,201 of the 2,096 authors here whose files we hold, read 2026-09-06
- Write a failing test before writing codein 43 of 1201, across 36 files
- Run the full test suitein 36 of 1201, across 35 files
- Test only one variable per experimentin 34 of 1201, across 17 files
- Read product marketing context before asking questionsin 34 of 1201, across 14 files
- Mock external dependenciesin 34 of 1201, across 30 files
- Define primary, secondary, and guardrail metricsin 33 of 1201, across 16 files
- Pre-determine sample size before startingin 31 of 1201, across 14 files
- Test behavior rather than implementationin 31 of 1201, across 29 files
- Formulate a hypothesis before designing a testin 30 of 1201, across 13 files
- Document every test hypothesis, variant, and resultin 29 of 1201, across 11 files
- Use descriptive test function namesin 25 of 1201, across 21 files
- Commit to the methodology without stopping earlyin 24 of 1201, across 8 files
Said here and by no other author read
- report median and IQR alongside mean and SD
- use SEM with t-distribution for confidence intervals
- check normality visually with Q-Q plots
- use Shapiro-Wilk as a guide for normality
- match statistical tests to paired or independent study designs
- apply BH-FDR correction for multiple testing
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.