Skip to content

SciPy - 统计

scipy.stats 模块是一个包含统计函数和概率分布的综合库。它提供了大量用于统计分析、假设检验(hypothesis testing)和处理随机变量(random variables)的工具。你可以在 Python 控制台中输入 help(scipy.stats) 或查阅 SciPy 官方文档,获取其功能的详细列表。

scipy.stats 中的概率分布(probability distributions)通过对象来表示。每个单变量分布(univariate distribution)是基础类的实例,分为连续型(continuous)或离散型(discrete):

类描述
rv_continuous连续随机变量(continuous random variables)的通用基类,旨在通过继承定义具体的分布(例如,正态分布、均匀分布、卡方分布)。
rv_discrete离散随机变量(discrete random variables)的通用基类,旨在通过继承定义具体的分布(例如,二项分布、泊松分布、几何分布)。
rv_histogram生成由观测数据的直方图定义的分布。

连续随机变量(continuous random variable)可以在给定范围内取任何值。正态分布(normal distribution),也称高斯分布(Gaussian distribution),是最常见的分布之一。在 scipy.stats 中,它由 norm 表示。关键字参数 loc 指定均值(mean,μ),scale 指定标准差(standard deviation,σ)。

norm 对象(以及其他分布对象)提供了以下方法:

  • .cdf(x): 累积分布函数 (Cumulative Distribution Function, CDF),$P(X \le x)$。
  • .pdf(x): 概率密度函数 (Probability Density Function, PDF)(用于连续型),或 .pmf(x): 概率质量函数 (Probability Mass Function, PMF)(用于离散型)。
  • .ppf(q): 分位数函数 (Percent Point Function, PPF)(CDF 的逆函数),查找满足 $P(X \le x) = q$ 的 $x$。
  • .rvs(size): 从分布中生成随机变量值(random variates,即样本)。
  • .mean(), .std(), .var(): 计算分布的理论矩(theoretical moments)。

让我们计算标准正态分布(均值=0,标准差=1)在几个点上的 CDF:

from scipy.stats import norm
import numpy as np
# Points at which to evaluate the CDF
points = np.array([-2., -1., 0., 1., 2., 3.])
# Calculate CDF values (default norm is standard normal: loc=0, scale=1)
cdf_values = norm.cdf(points)
print(f"CDF values for points {points}:\n{cdf_values}")
# Example for a normal distribution with mean=5, std=2
cdf_custom_norm = norm.cdf(points, loc=5, scale=2)
print(f"\nCDF for N(5,2) at points {points}:\n{cdf_custom_norm}")

输出将是:

CDF values for points [-2. -1. 0. 1. 2. 3.]:
[0.02275013 0.15865525 0.5 0.84134475 0.97724987 0.9986501 ]
CDF for N(5,2) at points [-2. -1. 0. 1. 2. 3.]:
[0.00023263 0.0013499 0.00620967 0.02275013 0.0668072 0.15865525]

要查找分布的中位数(median,即第 50 百分位数),请使用分位数函数(PPF):

from scipy.stats import norm
median_std_normal = norm.ppf(0.5) # For standard normal (loc=0, scale=1)
print(f"Median of standard normal distribution: {median_std_normal}")
median_custom_normal = norm.ppf(0.5, loc=10, scale=3)
print(f"Median of N(10,3) distribution: {median_custom_normal}")

输出:

Median of standard normal distribution: 0.0
Median of N(10,3) distribution: 10.0

要从分布生成随机样本,请使用带 size 参数的 rvs() 方法。为了获得可复现的结果,可以使用 np.random.seed() 设置随机种子,或者将 random_state 参数传递给 rvs()。

from scipy.stats import norm
import numpy as np
np.random.seed(42) # For reproducibility
random_samples = norm.rvs(loc=0, scale=1, size=5)
print(f"Random samples from N(0,1): {random_samples}")

输出(由于设置了种子,结果将是一致的):

Random samples from N(0,1): [ 0.49671415 -0.1382643 0.64768854 1.52302986 -0.23415337]

二项分布(Binomial Distribution)模拟了在固定次数 n 的独立伯努利试验(Bernoulli trials)中成功的次数,每次试验的成功概率为 p。在 scipy.stats 中,它由 binom 表示。

from scipy.stats import binom
import numpy as np
# Parameters for binomial distribution
n_trials = 10 # Number of trials
p_success = 0.3 # Probability of success in each trial
# Probability Mass Function (PMF): P(X=k)
# Probability of exactly 5 successes in 10 trials
k_successes = 5
pmf_value = binom.pmf(k_successes, n_trials, p_success)
print(f"PMF P(X={k_successes} | n={n_trials}, p={p_success}): {pmf_value:.4f}")
# Cumulative Distribution Function (CDF): P(X <= k)
# Probability of 5 or fewer successes
cdf_value = binom.cdf(k_successes, n_trials, p_success)
print(f"CDF P(X<={k_successes} | n={n_trials}, p={p_success}): {cdf_value:.4f}")
# Generate random samples
np.random.seed(123)
random_binom_samples = binom.rvs(n_trials, p_success, size=8)
print(f"Random samples from Binom({n_trials}, {p_success}): {random_binom_samples}")

输出:

PMF P(X=5 | n=10, p=0.3): 0.1029
CDF P(X<=5 | n=10, p=0.3): 0.9527
Random samples from Binom(10, 0.3): [2 3 4 1 3 2 2 4]

scipy.stats 还提供了对数据集(通常是 NumPy 数组)进行描述性统计(descriptive statistics)的函数。一些关键函数包括:

函数描述
describe(array, ...)计算输入数组的几个描述性统计量(例如,均值、方差、偏度、峰度)。
gmean(array, ...)计算几何均值(geometric mean)。
hmean(array, ...)计算调和均值(harmonic mean)。
kurtosis(array, ...)计算峰度(kurtosis)(尾部厚度的度量)。默认使用 Fisher 定义(正态分布峰度为 0.0)。
mode(array, ...)返回众数(mode)(出现频率最高的值)及其计数。
skew(array, ...)计算偏度(skew)(分布不对称性的度量)。
iqr(array, ...)计算四分位距(Interquartile Range, IQR = 第 75 百分位数 - 第 25 百分位数)。
sem(array, ...)计算均值标准误(Standard Error of the Mean, SEM)。
zscore(array, ...)计算每个值相对于样本均值和标准差的 z 分数(z-score)。

使用 describe 和其他基本统计量的示例:

from scipy import stats
import numpy as np
data = np.array([1, 2, 2, 3, 3, 3, 4, 4, 5, 10])
print(f"Data: {data}")
print(f"Mean: {np.mean(data):.2f}, Median: {np.median(data):.2f}")
print(f"Standard Deviation: {np.std(data):.2f}, Variance: {np.var(data):.2f}")
# Using scipy.stats.describe
descriptive_stats = stats.describe(data)
print("\nDescriptive statistics from stats.describe():")
print(f" Number of observations: {descriptive_stats.nobs}")
print(f" Min and max: {descriptive_stats.minmax}")
print(f" Mean: {descriptive_stats.mean:.2f}")
print(f" Variance: {descriptive_stats.variance:.2f}")
print(f" Skewness: {descriptive_stats.skewness:.2f}")
print(f" Kurtosis: {descriptive_stats.kurtosis:.2f}")

输出:

Data: [ 1 2 2 3 3 3 4 4 5 10]
Mean: 3.70, Median: 3.00
Standard Deviation: 2.26, Variance: 5.11
Descriptive statistics from stats.describe():
Number of observations: 10
Min and max: (1, 10)
Mean: 3.70
Variance: 5.68
Skewness: 1.70
Kurtosis: 2.64

注意:np.var 默认计算总体方差,而 stats.describe(以及 np.var(ddof=1))计算样本方差。这解释了在采用默认设置时,方差值可能存在的微小差异。

T 检验(T-tests)用于判断两组均值之间,或样本均值与已知总体均值之间是否存在显著差异(significant difference)。理解其前提假设(例如,正态性、样本独立性、某些检验需要方差相等)至关重要。

此检验检查单个样本的均值是否与已知或假设的总体均值(popmean)显著不同。这是一个双边检验(two-sided test),用于检验样本期望值(均值)等于给定总体均值的零假设(null hypothesis)。

from scipy import stats
import numpy as np
# Generate some sample data (e.g., from a normal distribution)
np.random.seed(101)
sample_data = stats.norm.rvs(loc=5.5, scale=1.5, size=30)
# Test if the sample mean is significantly different from 5.0
population_mean_h0 = 5.0
t_statistic, p_value = stats.ttest_1samp(sample_data, population_mean_h0)
print(f"Sample mean: {np.mean(sample_data):.2f}")
print(f"T-statistic: {t_statistic:.3f}")
print(f"P-value: {p_value:.3f}")
alpha = 0.05 # Significance level
if p_value < alpha:
print(f"Reject null hypothesis: Sample mean is significantly different from {population_mean_h0}.")
else:
print(f"Fail to reject null hypothesis: No significant difference from {population_mean_h0}.")

输出(由于随机抽样会有轻微差异,但种子使其可复现):

Sample mean: 5.60
T-statistic: 2.166
P-value: 0.038
Reject null hypothesis: Sample mean is significantly different from 5.0.

此检验比较两个独立样本的均值。零假设是两个独立样本具有相同的平均值(期望值)。默认情况下,它假定总体方差相等(equal_var=True)。如果方差不相等,请设置 equal_var=False 以执行 Welch’s T 检验(Welch’s t-test)。

from scipy import stats
import numpy as np
np.random.seed(42)
# Sample 1: e.g., scores from group A
sample1 = stats.norm.rvs(loc=5, scale=2, size=100)
# Sample 2: e.g., scores from group B (potentially different mean)
sample2 = stats.norm.rvs(loc=5.8, scale=2, size=100)
# Perform t-test assuming equal variances (default)
t_stat_eq_var, p_val_eq_var = stats.ttest_ind(sample1, sample2)
print(f"Independent T-test (equal variances assumed):")
print(f" T-statistic: {t_stat_eq_var:.3f}, P-value: {p_val_eq_var:.3f}")
# Perform Welch's t-test (variances not assumed equal)
# Let's create another sample with different variance for illustration
sample3 = stats.norm.rvs(loc=5.8, scale=4, size=100) # Different scale
t_stat_welch, p_val_welch = stats.ttest_ind(sample1, sample3, equal_var=False)
print(f"\nWelch's T-test (unequal variances assumed for sample1 vs sample3):")
print(f" T-statistic: {t_stat_welch:.3f}, P-value: {p_val_welch:.3f}")

输出(取决于种子):

Independent T-test (equal variances assumed):
T-statistic: -2.599, P-value: 0.010
Welch's T-test (unequal variances assumed for sample1 vs sample3):
T-statistic: -1.872, P-value: 0.063

解释 P 值:较小的 P 值(通常 < 0.05)表明如果在零假设为真的情况下,观测到的数据不太可能发生,因此导致拒绝零假设。较大的 P 值意味着数据与零假设一致。

scipy.stats 提供了更多分布(例如,uniform、poisson、chi2、f)和统计检验(例如,ANOVA 方差分析 (f_oneway)、Kolmogorov-Smirnov 检验 (kstest)、Shapiro-Wilk 正态性检验 (shapiro))。始终查阅文档以选择合适的检验并理解其前提假设。