Inferential Statistics Formulas in LaTeX, Python and Rust
✅ Copy-ready code for inferential statistics formulas in LaTeX, Python and Rust. For each formula, you'll see how to write it in LaTeX and how to calculate it in code. 😻
Every snippet runs on its own with no extra libraries, and the expected output is written in the comments. Use the tabs to switch between LaTeX, Python and Rust. To run a Rust snippet, save it as main.rs and run rustc main.rs, or paste it into the Rust Playground.
📚 Math Formulas in Code series
- Part 1: Algebra Formulas: exponents, logarithms, quadratics, lines and sequences
- Part 2: Linear Algebra Formulas: vectors, matrices and a neural network layer
- Part 3: Descriptive Statistics Formulas: mean, median, spread and correlation
- Part 4: Inferential Statistics Formulas (you are here): tests, confidence intervals, probability and regression
- Part 5: Calculus Formulas: derivatives, integrals and numerical methods
Just click the copy button in the top right corner of each code block!
Summary Overview
| # | Formula | Example |
|---|---|---|
| 1 | Standard Error of the Mean | |
| 2 | Normal Distribution Probability | |
| 3 | Confidence Interval for the Mean | |
| 4 | One-Sample z-Test | |
| 5 | One-Sample t-Test | |
| 6 | Two-Sample t-Test | |
| 7 | Chi-Squared Statistic | |
| 8 | Binomial Probability | 3 heads in 10 flips |
| 9 | Simple Linear Regression and R² | |
| 10 | Bayes' Theorem |
1. Standard Error of the Mean
Standard Error of the Mean
Formula:
The standard error shows how much the sample mean would change from sample to sample. Bigger samples give a smaller standard error.
Where it's used: confidence intervals, hypothesis tests, and error bars on charts.
$$
\text{SE} = \frac{s}{\sqrt{n}}
$$import math
def standard_error(values: list[float]) -> float:
n = len(values)
m = sum(values) / n
s = math.sqrt(sum((x - m) ** 2 for x in values) / (n - 1))
return s / math.sqrt(n)
print(round(standard_error([2, 4, 4, 4, 5, 5, 7, 9]), 4)) # 0.7559fn standard_error(values: &[f64]) -> f64 {
let n = values.len() as f64;
let m = values.iter().sum::<f64>() / n;
let s = (values.iter().map(|x| (x - m).powi(2)).sum::<f64>() / (n - 1.0)).sqrt();
s / n.sqrt()
}
fn main() {
let data = [2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0];
println!("{:.4}", standard_error(&data)); // 0.7559
}2. Normal Distribution Probability
Normal Distribution Probability
Formula:
Where it's used: turning z-scores into probabilities, and calculating p-values.
$$
\Phi(z) = P(Z \le z) = \frac{1}{2} \left[ 1 + \operatorname{erf}\left( \frac{z}{\sqrt{2}} \right) \right]
$$import math
def normal_cdf(z: float) -> float:
return 0.5 * (1 + math.erf(z / math.sqrt(2)))
print(round(normal_cdf(1.96), 4)) # 0.975
print(round(normal_cdf(0), 4)) # 0.5// Rust's standard library has no erf, so this uses the Abramowitz and Stegun
// approximation, accurate to about 7 decimal places.
fn erf(x: f64) -> f64 {
let sign = if x < 0.0 { -1.0 } else { 1.0 };
let x = x.abs();
let t = 1.0 / (1.0 + 0.3275911 * x);
let poly = t * (0.254829592 + t * (-0.284496736 + t * (1.421413741
+ t * (-1.453152027 + t * 1.061405429))));
sign * (1.0 - poly * (-x * x).exp())
}
fn normal_cdf(z: f64) -> f64 {
0.5 * (1.0 + erf(z / 2f64.sqrt()))
}
fn main() {
println!("{:.4}", normal_cdf(1.96)); // 0.9750
println!("{:.4}", normal_cdf(0.0)); // 0.5000
}3. Confidence Interval for the Mean
Confidence Interval for the Mean
Formula:
This uses the normal distribution, which works well for large samples. For small samples, use the t value for
Where it's used: reporting an estimate together with its uncertainty, such as model accuracy across test runs.
$$
\bar{x} \pm z^{*} \, \frac{s}{\sqrt{n}} \qquad 95\% \text{ CI}: \; z^{*} = 1.96
$$import math
def confidence_interval(mean: float, s: float, n: int, z: float = 1.96):
margin = z * s / math.sqrt(n)
return round(mean - margin, 2), round(mean + margin, 2)
print(confidence_interval(50, 10, 100)) # (48.04, 51.96)fn confidence_interval(mean: f64, s: f64, n: f64, z: f64) -> (f64, f64) {
let margin = z * s / n.sqrt();
(mean - margin, mean + margin)
}
fn main() {
let (low, high) = confidence_interval(50.0, 10.0, 100.0, 1.96);
println!("({:.2}, {:.2})", low, high); // (48.04, 51.96)
}4. One-Sample z-Test
One-Sample z-Test
Formula:
Use it when the population standard deviation
Where it's used: checking whether a sample mean is really different from a target value.
$$
z = \frac{\bar{x} - \mu_0}{\sigma / \sqrt{n}} \qquad p = 2 \big( 1 - \Phi(\lvert z \rvert) \big)
$$import math
def normal_cdf(z: float) -> float:
return 0.5 * (1 + math.erf(z / math.sqrt(2)))
def z_test(mean: float, mu0: float, sigma: float, n: int):
z = (mean - mu0) / (sigma / math.sqrt(n))
p = 2 * (1 - normal_cdf(abs(z)))
return round(z, 4), round(p, 4)
print(z_test(52, 50, 10, 100)) # (2.0, 0.0455)// Rust's standard library has no erf, so this uses the Abramowitz and Stegun
// approximation, accurate to about 7 decimal places.
fn erf(x: f64) -> f64 {
let sign = if x < 0.0 { -1.0 } else { 1.0 };
let x = x.abs();
let t = 1.0 / (1.0 + 0.3275911 * x);
let poly = t * (0.254829592 + t * (-0.284496736 + t * (1.421413741
+ t * (-1.453152027 + t * 1.061405429))));
sign * (1.0 - poly * (-x * x).exp())
}
fn normal_cdf(z: f64) -> f64 {
0.5 * (1.0 + erf(z / 2f64.sqrt()))
}
fn z_test(mean: f64, mu0: f64, sigma: f64, n: f64) -> (f64, f64) {
let z = (mean - mu0) / (sigma / n.sqrt());
let p = 2.0 * (1.0 - normal_cdf(z.abs()));
(z, p)
}
fn main() {
let (z, p) = z_test(52.0, 50.0, 10.0, 100.0);
println!("{:.4} {:.4}", z, p); // 2.0000 0.0455
}5. One-Sample t-Test
One-Sample t-Test
Formula:
Use it when you only have the sample standard deviation
Where it's used: testing a mean with a small sample, such as a few repeated experiments.
$$
t = \frac{\bar{x} - \mu_0}{s / \sqrt{n}}, \qquad \text{df} = n - 1
$$import math
def t_statistic(values: list[float], mu0: float):
n = len(values)
m = sum(values) / n
s = math.sqrt(sum((x - m) ** 2 for x in values) / (n - 1))
return round((m - mu0) / (s / math.sqrt(n)), 4), n - 1
print(t_statistic([5.1, 4.9, 5.3, 5.5, 5.0, 5.2], 5)) # (1.8898, 5)fn t_statistic(values: &[f64], mu0: f64) -> (f64, usize) {
let n = values.len() as f64;
let m = values.iter().sum::<f64>() / n;
let s = (values.iter().map(|x| (x - m).powi(2)).sum::<f64>() / (n - 1.0)).sqrt();
((m - mu0) / (s / n.sqrt()), values.len() - 1)
}
fn main() {
let (t, df) = t_statistic(&[5.1, 4.9, 5.3, 5.5, 5.0, 5.2], 5.0);
println!("{:.4} {}", t, df); // 1.8898 5
}6. Two-Sample t-Test
Two-Sample t-Test
Formula:
This is Welch's t-test, which does not assume the two groups have the same variance.
Where it's used: A/B testing, and comparing two models, groups or treatments.
$$
t = \frac{\bar{x}_1 - \bar{x}_2}{\sqrt{\frac{s_1^2}{n_1} + \frac{s_2^2}{n_2}}}
$$import math
def welch_t(a: list[float], b: list[float]) -> float:
def mean_var(x):
m = sum(x) / len(x)
return m, sum((v - m) ** 2 for v in x) / (len(x) - 1)
m1, v1 = mean_var(a)
m2, v2 = mean_var(b)
return (m1 - m2) / math.sqrt(v1 / len(a) + v2 / len(b))
print(round(welch_t([20, 22, 19, 24, 25], [18, 17, 20, 16, 19]), 4)) # 2.9814fn mean_var(x: &[f64]) -> (f64, f64) {
let n = x.len() as f64;
let m = x.iter().sum::<f64>() / n;
(m, x.iter().map(|v| (v - m).powi(2)).sum::<f64>() / (n - 1.0))
}
fn welch_t(a: &[f64], b: &[f64]) -> f64 {
let (m1, v1) = mean_var(a);
let (m2, v2) = mean_var(b);
(m1 - m2) / (v1 / a.len() as f64 + v2 / b.len() as f64).sqrt()
}
fn main() {
let a = [20.0, 22.0, 19.0, 24.0, 25.0];
let b = [18.0, 17.0, 20.0, 16.0, 19.0];
println!("{:.4}", welch_t(&a, &b)); // 2.9814
}7. Chi-Squared Statistic
Chi-Squared Statistic
Formula:
Where it's used: testing whether counts match what you expected, and whether categories are related.
$$
\chi^2 = \sum_{i} \frac{(O_i - E_i)^2}{E_i}
$$def chi_squared(observed: list[float], expected: list[float]) -> float:
return sum((o - e) ** 2 / e for o, e in zip(observed, expected))
print(chi_squared([18, 22, 20, 40], [25, 25, 25, 25])) # 12.32fn chi_squared(observed: &[f64], expected: &[f64]) -> f64 {
observed.iter().zip(expected).map(|(o, e)| (o - e).powi(2) / e).sum()
}
fn main() {
println!("{}", chi_squared(&[18.0, 22.0, 20.0, 40.0], &[25.0; 4])); // 12.32
}8. Binomial Probability
Binomial Probability
Formula:
Python has math.comb built in. Rust does not, so the code calculates the combination step by step.
Where it's used: the chance of exactly
$$
P(X = k) = \binom{n}{k} p^{k} (1 - p)^{n - k}
$$import math
def binomial_pmf(k: int, n: int, p: float) -> float:
return math.comb(n, k) * p**k * (1 - p)**(n - k)
print(round(binomial_pmf(3, 10, 0.5), 4)) # 0.1172fn comb(n: u64, k: u64) -> f64 {
(0..k).fold(1.0, |acc, i| acc * (n - i) as f64 / (i + 1) as f64)
}
fn binomial_pmf(k: u64, n: u64, p: f64) -> f64 {
comb(n, k) * p.powi(k as i32) * (1.0 - p).powi((n - k) as i32)
}
fn main() {
println!("{:.4}", binomial_pmf(3, 10, 0.5)); // 0.1172
}9. Simple Linear Regression and R²
Simple Linear Regression and R²
Formula:
The fitted line is
Where it's used: predicting one variable from another, the first model in Machine Learning.
$$
\beta_1 = \frac{\sum_{i} (x_i - \bar{x})(y_i - \bar{y})}{\sum_{i} (x_i - \bar{x})^2} \qquad \beta_0 = \bar{y} - \beta_1 \bar{x} \qquad R^2 = 1 - \frac{\sum_{i} (y_i - \hat{y}_i)^2}{\sum_{i} (y_i - \bar{y})^2}
$$def linear_regression(x: list[float], y: list[float]):
n = len(x)
mx, my = sum(x) / n, sum(y) / n
b1 = sum((a - mx) * (b - my) for a, b in zip(x, y)) / sum((a - mx) ** 2 for a in x)
b0 = my - b1 * mx
ss_res = sum((b - (b0 + b1 * a)) ** 2 for a, b in zip(x, y))
ss_tot = sum((b - my) ** 2 for b in y)
return round(b1, 4), round(b0, 4), round(1 - ss_res / ss_tot, 4)
print(linear_regression([1, 2, 3, 4, 5], [2, 4, 5, 4, 5])) # (0.6, 2.2, 0.6)fn linear_regression(x: &[f64], y: &[f64]) -> (f64, f64, f64) {
let n = x.len() as f64;
let mx = x.iter().sum::<f64>() / n;
let my = y.iter().sum::<f64>() / n;
let b1 = x.iter().zip(y).map(|(a, b)| (a - mx) * (b - my)).sum::<f64>()
/ x.iter().map(|a| (a - mx).powi(2)).sum::<f64>();
let b0 = my - b1 * mx;
let ss_res: f64 = x.iter().zip(y).map(|(a, b)| (b - (b0 + b1 * a)).powi(2)).sum();
let ss_tot: f64 = y.iter().map(|b| (b - my).powi(2)).sum();
(b1, b0, 1.0 - ss_res / ss_tot)
}
fn main() {
let (b1, b0, r2) = linear_regression(&[1.0, 2.0, 3.0, 4.0, 5.0], &[2.0, 4.0, 5.0, 4.0, 5.0]);
println!("{:.4} {:.4} {:.4}", b1, b0, r2); // 0.6000 2.2000 0.6000
}10. Bayes' Theorem
Bayes' Theorem
Formula:
Example: a disease affects 1% of people, the test catches 99% of cases, and gives a false positive 5% of the time. A positive result still means only about a 16.67% chance of having the disease.
Where it's used: medical tests, spam filters and Naive Bayes classifiers.
$$
P(D \mid +) = \frac{P(+ \mid D) \, P(D)}{P(+ \mid D) \, P(D) + P(+ \mid \neg D) \, P(\neg D)}
$$def bayes(prior: float, sensitivity: float, false_positive: float) -> float:
true_pos = sensitivity * prior
false_pos = false_positive * (1 - prior)
return true_pos / (true_pos + false_pos)
print(round(bayes(0.01, 0.99, 0.05), 4)) # 0.1667fn bayes(prior: f64, sensitivity: f64, false_positive: f64) -> f64 {
let true_pos = sensitivity * prior;
let false_pos = false_positive * (1.0 - prior);
true_pos / (true_pos + false_pos)
}
fn main() {
println!("{:.4}", bayes(0.01, 0.99, 0.05)); // 0.1667
}