ZetCode

Python SciPy Stats

最后修改于 2025年3月8日

本教程将使用 SciPy 库中的 scipy.stats 模块,探索 Python 中的统计分析,该模块非常适合高级数据科学任务。

scipy.stats 模块提供了用于描述性统计、概率分布和假设检验的工具,其功能远远超出了 Python 本身 statistics 模块的基本能力。

安装 SciPy

$ pip install scipy

使用 pip 安装 SciPy,以便访问 stats 模块及其强大的统计函数。

SciPy Stats 均值和中位数

均值是数据集的平均值,而中位数是排序后数据集的中间值,它对异常值不敏感。

mean_median.py
#!/usr/bin/python

from scipy import stats
import numpy as np

sales = [1200, 1500, 1300, 1700, 5000]  # Monthly sales in USD
mean = np.mean(sales)
median = np.median(sales)

print(f"Mean Sales: ${mean:.2f}")
print(f"Median Sales: ${median:.2f}")

我们导入 scipy.statsnumpy 以进行高效的数组操作。sales 列表代表月度收入。我们使用 np.meannp.median 进行计算。

均值 (2,340 美元) 受异常值 (5,000 美元) 的影响而偏斜,而中位数 (1,500 美元) 更能反映典型销售情况,这表明 scipy.stats 与 NumPy 的集成。

$ ./mean_median.py
Mean Sales: $2340.00
Median Sales: $1500.00

SciPy Stats 众数

众数标识数据集中最频繁出现的值,适用于分类或离散数据分析。

mode.py
#!/usr/bin/python

from scipy import stats

ratings = [5, 4, 5, 3, 4, 5, 2, 4, 5]  # Customer satisfaction scores
mode_result = stats.mode(ratings)

print(f"Mode: {mode_result.mode[0]}, Count: {mode_result.count[0]}")

ratings 列表模拟客户反馈(1-5 分制)。stats.mode 返回一个 ModeResult 对象,其中包含 mode(最频繁出现的值)和 count(其频率)。在这里,5 出现了 4 次。

$ ./mode.py
Mode: 5, Count: 4

SciPy Stats 方差和标准差

方差衡量数据围绕均值的平方差的平均值。标准差是方差的平方根,表示在原始单位下的典型偏差。

var_stdev.py
#!/usr/bin/python

from scipy import stats
import numpy as np

temps = [22.5, 23.0, 21.8, 24.1, 22.9]  # Daily temperatures (°C)
variance = np.var(temps, ddof=1)  # Sample variance
stdev = np.std(temps, ddof=1)     # Sample standard deviation

print(f"Variance: {variance:.2f} °C²")
print(f"Standard Deviation: {stdev:.2f} °C")

我们使用温度数据计算样本方差和标准差 (ddof=1 表示样本,而非总体)。方差 (0.85 °C²) 显示了以平方单位表示的离散度,而标准差 (0.92 °C) 更易于解释。

$ ./var_stdev.py
Variance: 0.85 °C²
Standard Deviation: 0.92 °C

SciPy Stats 正态分布

正态分布用钟形曲线模拟连续数据,由均值和标准差定义,在自然现象中很常见。

normal_dist.py
#!/usr/bin/python

from scipy import stats

# IQ scores: mean=100, std=15
iq_dist = stats.norm(loc=100, scale=15)
prob_above_120 = 1 - iq_dist.cdf(120)
sample = iq_dist.rvs(size=5)

print(f"P(IQ > 120): {prob_above_120:.3f}")
print(f"Random IQs: {sample}")

我们用正态分布 (loc = 均值, scale = 标准差) 来模拟智商分数。cdf(120) 给出 120 以下的累积概率,因此 1 - cdf 是超过 120 的概率。rvs 生成随机样本。

这对于心理学或教育学中的模拟或概率评估很有用。

$ ./normal_dist.py
P(IQ > 120): 0.091
Random IQs: [ 95.2 112.7  88.4 104.1  99.6]  # Values may vary

SciPy Stats T 检验

T 检验比较均值,以评估差异是否具有统计学意义,广泛用于假设检验。

ttest.py
#!/usr/bin/python

from scipy import stats

group1 = [85, 88, 90, 87, 86]  # Test scores, method A
group2 = [90, 92, 89, 94, 91]  # Test scores, method B
t_stat, p_val = stats.ttest_ind(group1, group2)

print(f"T-Statistic: {t_stat:.2f}")
print(f"P-Value: {p_val:.3f}")

我们比较两种教学方法的测试成绩。ttest_ind 执行独立样本 t 检验,返回 t 统计量和 p 值。较低的 p 值 ( < 0.05) 表明存在显著差异。

这里,p=0.013 表明方法 B 可能会提高成绩,这是教育研究中的常见分析。

$ ./ttest.py
T-Statistic: -2.98
P-Value: 0.013

SciPy Stats 相关性

相关系数衡量两个变量之间的线性关系,范围从 -1(完全负相关)到 1(完全正相关)。

correlation.py
#!/usr/bin/python

from scipy import stats

hours = [2, 3, 4, 5, 6]         # Study hours
scores = [65, 70, 75, 85, 90]  # Exam scores
r, p = stats.pearsonr(hours, scores)

print(f"Pearson Correlation: {r:.2f}")
print(f"P-Value: {p:.3f}")

我们测试学习时间和考试成绩之间是否存在相关性。pearsonr 计算皮尔逊相关系数和 p 值。较高的 r (0.97) 和较低的 p 值 (0.006) 证实了强正相关关系。

$ ./correlation.py
Pearson Correlation: 0.97
P-Value: 0.006

SciPy Stats 峰度

峰度衡量分布的“尾部”特征,指示数据与正态分布相比是具有重尾还是轻尾。正峰度意味着重尾(更多异常值),而负峰度意味着轻尾。

kurtosis.py
#!/usr/bin/python

from scipy import stats
import numpy as np

# Daily stock returns (%) for a tech company
returns = [0.5, -0.3, 1.2, -2.5, 0.8, 3.1, -1.8, 0.2, 4.0, -3.2]
kurt = stats.kurtosis(returns)

print(f"Kurtosis of Returns: {kurt:.2f}")

我们分析每日股票收益,这是一个常见的金融数据集。returns 列表模拟了 10 天内股票价格的百分比变化。stats.kurtosis 计算了超额峰度(相对于正态分布,其中峰度 = 0)。

0.73 的峰度表明比正态分布具有更重的尾部,这表明价格波动更极端——这对于金融风险评估很有用。

$ ./kurtosis.py
Kurtosis of Returns: 0.73

SciPy Stats 偏度

偏度衡量分布的非对称性。正偏度意味着右尾较长(例如,收入数据),而负偏度表示左尾较长(例如,失效时间)。

skew.py
#!/usr/bin/python

from scipy import stats
import numpy as np

# Annual incomes (thousands USD) in a small town
incomes = [25, 30, 35, 40, 45, 50, 60, 80, 120, 200]
skewness = stats.skew(incomes)

print(f"Skewness of Incomes: {skewness:.2f}")

incomes 列表代表年收入,这是收入数据中由于少数高收入者而出现的典型情况。stats.skew 计算偏度。正值 (1.46) 证实了右偏分布,这在收入研究中很常见。

这有助于经济学家理解财富分配并识别人口中的不平等趋势。

$ ./skew.py
Skewness of Incomes: 1.46

SciPy Stats 查找重复项

find_repeats 函数可识别数组中的重复值及其计数,有助于发现离散数据中的模式或异常。

find_repeats.py
#!/usr/bin/python

from scipy import stats
import numpy as np

# Customer purchase counts in a week
purchases = [1, 2, 3, 2, 4, 1, 2, 5, 1, 3]
repeats = stats.find_repeats(purchases)

print(f"Repeated Values: {repeats.values}")
print(f"Counts: {repeats.counts}")

purchases 数组跟踪每个客户购买的商品数量。stats.find_repeats 返回一个 FindRepeatsResult 对象,其中包含 values(重复的数字)和 counts(它们重复的次数)。

这里,1 和 2 出现了多次(3 次和 3 次),这表明小额购买的频率很高。这对于零售分析以优化库存或促销活动很有价值。

$ ./find_repeats.py
Repeated Values: [1. 2. 3.]
Counts: [3 3 2]

SciPy Stats 描述

describe 函数提供数据集的摘要,包括计数、均值、方差、偏度、峰度以及最小值/最大值,从而提供快速概览。

describe.py
#!/usr/bin/python

from scipy import stats
import numpy as np

# Patient wait times (minutes) in a clinic
wait_times = [10, 15, 12, 20, 25, 18, 30, 22, 35, 40]
summary = stats.describe(wait_times)

print(f"Number of Observations: {summary.nobs}")
print(f"Mean: {summary.mean:.2f} minutes")
print(f"Variance: {summary.variance:.2f} min²")
print(f"Skewness: {summary.skewness:.2f}")
print(f"Kurtosis: {summary.kurtosis:.2f}")
print(f"Min: {summary.minmax[0]}, Max: {summary.minmax[1]}")

wait_times 列表模拟了医疗保健环境中的患者等待时间。stats.describe 返回一个 DescribeResult 对象,其中包含关键统计数据,可以通过 nobs(计数)和 mean 等属性进行访问。

摘要显示平均等待时间为 22.7 分钟,轻微正偏度 (0.47) 和负峰度 (-0.73),表明分布更平坦。这有助于诊所评估服务效率和患者体验。

$ ./describe.py
Number of Observations: 10
Mean: 22.70 minutes
Variance: 90.46 min²
Skewness: 0.47
Kurtosis: -0.73
Min: 10, Max: 40

SciPy Stats 线性回归

线性回归模拟因变量与一个或多个自变量之间的关系,并预测趋势。

regression.py
#!/usr/bin/python

from scipy import stats

ad_spend = [100, 200, 300, 400, 500]  # Ad budget ($)
sales = [1200, 1500, 1800, 2100, 2400] # Sales ($)
slope, intercept, r, p, se = stats.linregress(ad_spend, sales)

print(f"Slope: {slope:.2f}, Intercept: {intercept:.2f}")
print(f"R²: {r**2:.3f}, P-Value: {p:.3f}")

我们根据广告支出对销售额进行建模。linregress 返回斜率、截距、相关系数 (r)、p 值和标准误差。斜率 (2.4) 表明每花费 1 美元的广告费可以带来 2.40 美元的销售额。

R² (1.0) 表明完美拟合,但实际数据会显示更多变异。这对于营销分析很有用。

$ ./regression.py
Slope: 2.40, Intercept: 960.00
R²: 1.000, P-Value: 0.000

最佳实践

来源

SciPy stats 文档

本教程展示了 scipy.stats 在高级统计分析中的应用,从分布到回归,并附有实际示例。

作者

我叫 Jan Bodnar,是一名充满热情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有超过十年的经验。

列出所有 Python 教程