statistics — 数学统计函数

在 3.4 版本加入。

源代码: Lib/statistics.py


此模块提供了用于计算数值型(Real 值)数据的数学统计量的函数。

此模块并非旨在与 NumPy、SciPy 等第三方库,或 Minitab、SAS 和 Matlab 等面向专业统计学家的专有全功能统计软件包竞争。它的目标是图形计算器和科学计算器的级别。

除非明确指出,这些函数支持 intfloatDecimalFraction。目前不支持其他类型(无论是否在数值类型层次结构中)的行为。混合类型的集合也未定义且取决于具体实现。如果您的输入数据包含混合类型,您可以使用 map() 来确保一致的结果,例如:map(float, input_data)

一些数据集使用 NaN(非数字)值来表示缺失数据。由于 NaN 具有不寻常的比较语义,它们会在对数据进行排序或计数发生的统计函数中导致令人惊讶或未定义的行为。受影响的函数包括 median()median_low()median_high()median_grouped()mode()multimode()quantiles()NaN 值应在调用这些函数之前进行剥离。

>>> from statistics import median
>>> from math import isnan
>>> from itertools import filterfalse

>>> data = [20.7, float('NaN'),19.2, 18.3, float('NaN'), 14.4]
>>> sorted(data)  # This has surprising behavior
[20.7, nan, 14.4, 18.3, 19.2, nan]
>>> median(data)  # This result is unexpected
16.35

>>> sum(map(isnan, data))    # Number of missing values
2
>>> clean = list(filterfalse(isnan, data))  # Strip NaN values
>>> clean
[20.7, 19.2, 18.3, 14.4]
>>> sorted(clean)  # Sorting now works as expected
[14.4, 18.3, 19.2, 20.7]
>>> median(clean)       # This result is now well defined
18.75

平均值和中心位置度量

这些函数计算总体或样本的平均值或典型值。

mean()

数据的算术平均值(“平均数”)。

fmean()

快速浮点算术平均值,可选加权。

geometric_mean()

数据的几何平均值。

harmonic_mean()

数据的调和平均值。

kde()

估计数据的概率密度分布。

kde_random()

从 kde() 生成的 PDF 中进行随机抽样。

median()

数据的中位数(中间值)。

median_low()

数据的低中位数。

median_high()

数据的高中位数。

median_grouped()

分组数据的中位数(第 50 百分位数)。

mode()

离散或名义数据的单一众数(最常见值)。

multimode()

离散或名义数据的众数列表(最常见值)。

quantiles()

将数据分成等概率区间。

离散度度量

这些函数计算总体或样本偏离典型值或平均值的程度。

pstdev()

数据的总体标准差。

pvariance()

数据的总体方差。

stdev()

数据的样本标准差。

variance()

数据的样本方差。

两个输入之间关系的统计量

这些函数计算两个输入之间关系的统计量。

covariance()

两个变量的样本协方差。

correlation()

皮尔逊和斯皮尔曼相关系数。

linear_regression()

简单线性回归的斜率和截距。

函数详情

注意:这些函数不要求传入的数据已排序。然而,为了阅读方便,大多数示例都展示了排序后的序列。

statistics.mean(data)

返回 data 的样本算术平均值,data 可以是序列或可迭代对象。

算术平均值是数据之和除以数据点数量。它通常被称为“平均值”,尽管它是许多不同数学平均值中的一种。它是数据中心位置的度量。

如果 data 为空,将引发 StatisticsError

一些使用示例

>>> mean([1, 2, 3, 4, 4])
2.8
>>> mean([-1.0, 2.5, 3.25, 5.75])
2.625

>>> from fractions import Fraction as F
>>> mean([F(3, 7), F(1, 21), F(5, 3), F(1, 3)])
Fraction(13, 21)

>>> from decimal import Decimal as D
>>> mean([D("0.5"), D("0.75"), D("0.625"), D("0.375")])
Decimal('0.5625')

备注

平均值受异常值影响很大,不一定是数据点的典型示例。对于更稳健(尽管效率较低)的中心趋势度量,请参见 median()

样本均值提供了真实总体均值的无偏估计,因此在所有可能的样本上取平均值时,mean(sample) 将收敛于整个总体的真实均值。如果 data 表示整个总体而不是样本,则 mean(data) 等同于计算真实总体均值 μ。

statistics.fmean(data, weights=None)

data 转换为浮点数并计算算术平均值。

这比 mean() 函数运行得更快,并且它总是返回一个 floatdata 可以是序列或可迭代对象。如果输入数据集为空,则会引发 StatisticsError

>>> fmean([3.5, 4.0, 5.25])
4.25

支持可选加权。例如,一位教授通过将测验权重设为 20%,作业权重设为 20%,期中考试权重设为 30%,期末考试权重设为 30% 来评定课程成绩。

>>> grades = [85, 92, 83, 91]
>>> weights = [0.20, 0.20, 0.30, 0.30]
>>> fmean(grades, weights)
87.6

如果提供了 weights,它的长度必须与 data 相同,否则将引发 ValueError

在 3.8 版本加入。

3.11 版本中的更改: 添加了对 weights 的支持。

statistics.geometric_mean(data)

data 转换为浮点数并计算几何平均值。

几何平均值使用值的乘积(与使用值的总和的算术平均值相反)来表示 data 的中心趋势或典型值。

如果输入数据集为空、包含零或包含负值,则会引发 StatisticsErrordata 可以是序列或可迭代对象。

未采取特殊措施以获得精确结果。(然而,未来可能会改变。)

>>> round(geometric_mean([54, 24, 36]), 1)
36.0

在 3.8 版本加入。

statistics.harmonic_mean(data, weights=None)

返回 data 的调和平均值,data 是实数值的序列或可迭代对象。如果省略 weights 或为 None,则假定为等权重。

调和平均值是数据倒数算术 mean() 的倒数。例如,三个值 abc 的调和平均值将等同于 3/(1/a + 1/b + 1/c)。如果其中一个值为零,结果将为零。

调和平均值是一种平均值,是数据中心位置的度量。它通常适用于平均比率或速率,例如速度。

假设一辆车以 40 公里/小时的速度行驶 10 公里,然后以 60 公里/小时的速度行驶另外 10 公里。平均速度是多少?

>>> harmonic_mean([40, 60])
48.0

假设一辆车以 40 公里/小时的速度行驶 5 公里,然后交通疏导后,以 60 公里/小时的速度行驶剩余的 30 公里。平均速度是多少?

>>> harmonic_mean([40, 60], weights=[5, 30])
56.0

如果 data 为空,任何元素小于零,或者加权和不为正,则引发 StatisticsError

当前算法在输入中遇到零时会提前退出。这意味着后续输入不会进行有效性检查。(此行为未来可能会改变。)

在 3.6 版本加入。

3.10 版本中的更改: 添加了对 weights 的支持。

statistics.kde(data, h, kernel='normal', *, cumulative=False)

核密度估计 (KDE):从离散样本创建连续概率密度函数或累积分布函数。

基本思想是使用核函数对数据进行平滑处理,以帮助从样本中推断总体。

平滑程度由称为带宽的缩放参数 h 控制。较小的值强调局部特征,而较大的值产生更平滑的结果。

kernel 决定了样本数据点的相对权重。通常,核形状的选择不如影响力更大的带宽平滑参数重要。

对每个样本点都给予一定权重的核包括 normal (gauss)、logisticsigmoid

仅对带宽内的样本点给予权重的核包括 rectangular (uniform)、triangularparabolic (epanechnikov)、quartic (biweight)、triweightcosine

如果 cumulative 为真,将返回一个累积分布函数。

如果 data 序列为空,将引发 StatisticsError

维基百科有一个示例,我们可以使用 kde() 从少量样本中生成并绘制估计的概率密度函数。

>>> sample = [-2.1, -1.3, -0.4, 1.9, 5.1, 6.2]
>>> f_hat = kde(sample, h=1.5)
>>> xarr = [i/100 for i in range(-750, 1100)]
>>> yarr = [f_hat(x) for x in xarr]

xarryarr 中的点可用于制作 PDF 图。

Scatter plot of the estimated probability density function.

在 3.13 版本加入。

statistics.kde_random(data, h, kernel='normal', *, seed=None)

返回一个函数,该函数从 kde(data, h, kernel) 产生的估计概率密度函数中进行随机选择。

提供 seed 允许可重现的选择。将来,随着更精确的核逆 CDF 估计的实现,这些值可能会略有变化。seed 可以是整数、浮点数、字符串或字节。

如果 data 序列为空,将引发 StatisticsError

继续 kde() 的示例,我们可以使用 kde_random() 从估计的概率密度函数中生成新的随机选择。

>>> data = [-2.1, -1.3, -0.4, 1.9, 5.1, 6.2]
>>> rand = kde_random(data, h=1.5, seed=8675309)
>>> new_selections = [rand() for i in range(10)]
>>> [round(x, 1) for x in new_selections]
[0.7, 6.2, 1.2, 6.9, 7.0, 1.8, 2.5, -0.5, -1.8, 5.6]

在 3.13 版本加入。

statistics.median(data)

使用常见的“中间两个值的平均值”方法,返回数值数据的中位数(中间值)。如果 data 为空,则引发 StatisticsErrordata 可以是序列或可迭代对象。

中位数是衡量中心位置的稳健指标,受异常值影响较小。当数据点数量为奇数时,返回中间数据点。

>>> median([1, 3, 5])
3

当数据点数量为偶数时,通过取中间两个值的平均值来插值中位数。

>>> median([1, 3, 5, 7])
4.0

这适用于您的数据是离散的,并且您不介意中位数可能不是实际数据点的情况。

如果数据是序数(支持排序操作)但不是数值(不支持加法),请考虑使用 median_low()median_high()

statistics.median_low(data)

返回数值数据的低中位数。如果 data 为空,则引发 StatisticsErrordata 可以是序列或可迭代对象。

低中位数始终是数据集的成员。当数据点数量为奇数时,返回中间值。当数据点数量为偶数时,返回两个中间值中较小的一个。

>>> median_low([1, 3, 5])
3
>>> median_low([1, 3, 5, 7])
3

当您的数据是离散的并且您希望中位数是实际数据点而不是插值时,请使用低中位数。

statistics.median_high(data)

返回数据的高中位数。如果 data 为空,则引发 StatisticsErrordata 可以是序列或可迭代对象。

高中位数始终是数据集的成员。当数据点数量为奇数时,返回中间值。当数据点数量为偶数时,返回两个中间值中较大的一个。

>>> median_high([1, 3, 5])
3
>>> median_high([1, 3, 5, 7])
5

当您的数据是离散的并且您希望中位数是实际数据点而不是插值时,请使用高中位数。

statistics.median_grouped(data, interval=1.0)

估计已分组或分箱在连续固定宽度区间中点附近的数值数据的中位数。

data 可以是任何可迭代的数值数据,其中每个值都恰好是箱子的中点。必须至少存在一个值。

interval 是每个箱子的宽度。

例如,人口统计信息可能已被汇总为连续的十年年龄组,每个年龄组由区间的 5 年中点表示。

>>> from collections import Counter
>>> demographics = Counter({
...    25: 172,   # 20 to 30 years old
...    35: 484,   # 30 to 40 years old
...    45: 387,   # 40 to 50 years old
...    55:  22,   # 50 to 60 years old
...    65:   6,   # 60 to 70 years old
... })
...

第 50 百分位数(中位数)是 1071 名成员队列中的第 536 人。该人处于 30 至 40 岁年龄组。

常规的 median() 函数会假定三十多岁年龄组中的每个人都正好是 35 岁。一个更站得住脚的假设是该年龄组的 484 名成员均匀分布在 30 岁到 40 岁之间。为此,我们使用 median_grouped()

>>> data = list(demographics.elements())
>>> median(data)
35
>>> round(median_grouped(data, interval=10), 1)
37.5

调用者有责任确保数据点以 interval 的精确倍数分隔。这对于获得正确结果至关重要。该函数不检查此前提条件。

输入可以是任何可在插值步骤中强制转换为浮点数的数值类型。

statistics.mode(data)

从离散或名义 data 中返回单个最常见的数据点。众数(如果存在)是最典型的值,并作为中心位置的度量。

如果有多个具有相同频率的众数,则返回在 data 中遇到的第一个。如果需要其中最小或最大的众数,请使用 min(multimode(data))max(multimode(data))。如果输入 data 为空,则引发 StatisticsError

mode 假定离散数据并返回单个值。这是众数的标准处理方法,通常在学校教授。

>>> mode([1, 1, 2, 3, 3, 3, 3, 4])
3

众数是此包中唯一一个也适用于名义(非数值)数据的统计量。

>>> mode(["red", "blue", "blue", "red", "green", "red", "red"])
'red'

仅支持可哈希输入。要处理 set 类型,请考虑转换为 frozenset。要处理 list 类型,请考虑转换为 tuple。对于混合或嵌套输入,请考虑使用这种较慢的二次算法,它只依赖于相等性测试:max(data, key=data.count)

3.8 版本中的更改: 现在通过返回遇到的第一个众数来处理多众数数据集。以前,当找到多个众数时,它会引发 StatisticsError

statistics.multimode(data)

返回 data 中出现频率最高的值列表,按它们在 data 中首次出现的顺序排列。如果存在多个众数,将返回多个结果;如果 data 为空,则返回空列表。

>>> multimode('aabbbbccddddeeffffgg')
['b', 'd', 'f']
>>> multimode('')
[]

在 3.8 版本加入。

statistics.pstdev(data, mu=None)

返回总体标准差(总体方差的平方根)。有关参数和其他详细信息,请参见 pvariance()

>>> pstdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75])
0.986893273527251
statistics.pvariance(data, mu=None)

返回 data 的总体方差,data 是一个非空序列或实数值的可迭代对象。方差,或关于均值的二阶矩,是数据变异性(分散或离散)的度量。大方差表示数据分散,小方差表示数据紧密聚集在均值附近。

如果给定可选的第二个参数 mu,它应该是 data总体均值。它也可以用于计算不是均值的点的二阶矩。如果它缺失或为 None(默认),则自动计算算术平均值。

使用此函数计算整个总体的方差。要从样本估计方差,variance() 函数通常是更好的选择。

如果 data 为空,则引发 StatisticsError

示例:

>>> data = [0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25]
>>> pvariance(data)
1.25

如果您已经计算了数据的均值,可以将其作为可选的第二个参数 mu 传入,以避免重新计算。

>>> mu = mean(data)
>>> pvariance(data, mu)
1.25

支持 Decimal 和 Fraction 类型。

>>> from decimal import Decimal as D
>>> pvariance([D("27.5"), D("30.25"), D("30.25"), D("34.5"), D("41.75")])
Decimal('24.815')

>>> from fractions import Fraction as F
>>> pvariance([F(1, 4), F(5, 4), F(1, 2)])
Fraction(13, 72)

备注

当对整个总体调用时,这给出总体方差 σ²。当对样本调用时,这是有偏的样本方差 s²,也称为具有 N 自由度的方差。

如果您以某种方式知道真实的总体均值 μ,您可以使用此函数计算样本的方差,将已知的总体均值作为第二个参数。假设数据点是总体的随机样本,结果将是总体方差的无偏估计。

statistics.stdev(data, xbar=None)

返回样本标准差(样本方差的平方根)。有关参数和其他详细信息,请参见 variance()

>>> stdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75])
1.0810874155219827
statistics.variance(data, xbar=None)

返回 data 的样本方差,data 是一个包含至少两个实数值的可迭代对象。方差,或关于均值的二阶矩,是数据变异性(分散或离散)的度量。大方差表示数据分散;小方差表示数据紧密聚集在均值附近。

如果给定可选的第二个参数 xbar,它应该是 data样本均值。如果它缺失或为 None(默认),则自动计算均值。

当您的数据是总体的一个样本时,请使用此函数。要计算整个总体的方差,请参见 pvariance()

如果 data 的值少于两个,则引发 StatisticsError

示例:

>>> data = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5]
>>> variance(data)
1.3720238095238095

如果您已经计算了数据的样本均值,可以将其作为可选的第二个参数 xbar 传入,以避免重新计算。

>>> m = mean(data)
>>> variance(data, m)
1.3720238095238095

此函数不会尝试验证您是否将实际均值作为 xbar 传入。对 xbar 使用任意值可能导致无效或不可能的结果。

支持 Decimal 和 Fraction 值。

>>> from decimal import Decimal as D
>>> variance([D("27.5"), D("30.25"), D("30.25"), D("34.5"), D("41.75")])
Decimal('31.01875')

>>> from fractions import Fraction as F
>>> variance([F(1, 6), F(1, 2), F(5, 3)])
Fraction(67, 108)

备注

这是带有贝塞尔校正的样本方差 s²,也称为具有 N-1 自由度的方差。假设数据点具有代表性(例如独立同分布),结果应该是真实总体方差的无偏估计。

如果您以某种方式知道实际总体均值 μ,您应该将其作为 mu 参数传递给 pvariance() 函数以获取样本的方差。

statistics.quantiles(data, *, n=4, method='exclusive')

data 分成 n 个等概率的连续区间。返回一个由 n - 1 个分隔这些区间的切点组成的列表。

n 设置为 4 表示四分位数(默认)。将 n 设置为 10 表示十分位数。将 n 设置为 100 表示百分位数,它提供 99 个切点,将 data 分成 100 个大小相等的组。如果 n 不小于 1,则引发 StatisticsError

data 可以是包含样本数据的任何可迭代对象。为了获得有意义的结果,data 中的数据点数量应大于 n。如果至少没有一个数据点,则引发 StatisticsError

切点是从最近的两个数据点进行线性插值的。例如,如果一个切点落在两个样本值 100112 之间距离的三分之一处,则切点将计算为 104

计算分位数的 method 可以根据 data 是否包含或排除总体中最低和最高可能值而有所不同。

默认 method 为“exclusive”,用于从可能包含比样本中发现的更极端值的总体中采样的数据。低于 m 个排序数据点中的第 i 个的总体部分计算为 i / (m + 1)。给定九个样本值,该方法对它们进行排序并分配以下百分位数:10%、20%、30%、40%、50%、60%、70%、80%、90%。

method 设置为“inclusive”用于描述总体数据或已知包含总体中最极端值的样本。data 中的最小值被视为第 0 百分位数,最大值被视为第 100 百分位数。低于 m 个排序数据点中的第 i 个的总体部分计算为 (i - 1) / (m - 1)。给定 11 个样本值,该方法对它们进行排序并分配以下百分位数:0%、10%、20%、30%、40%、50%、60%、70%、80%、90%、100%。

# Decile cut points for empirically sampled data
>>> data = [105, 129, 87, 86, 111, 111, 89, 81, 108, 92, 110,
...         100, 75, 105, 103, 109, 76, 119, 99, 91, 103, 129,
...         106, 101, 84, 111, 74, 87, 86, 103, 103, 106, 86,
...         111, 75, 87, 102, 121, 111, 88, 89, 101, 106, 95,
...         103, 107, 101, 81, 109, 104]
>>> [round(q, 1) for q in quantiles(data, n=10)]
[81.0, 86.2, 89.0, 99.4, 102.5, 103.6, 106.0, 109.8, 111.0]

在 3.8 版本加入。

3.13 版本中的更改: 不再对仅包含一个数据点的输入引发异常。这允许一次构建一个样本点的分位数估计,并随着每个新数据点逐渐变得更精确。

statistics.covariance(x, y, /)

返回两个输入 xy 的样本协方差。协方差是衡量两个输入的联合变异性的指标。

两个输入必须长度相同(不少于两个),否则会引发 StatisticsError

示例:

>>> x = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> y = [1, 2, 3, 1, 2, 3, 1, 2, 3]
>>> covariance(x, y)
0.75
>>> z = [9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> covariance(x, z)
-7.5
>>> covariance(z, x)
-7.5

在 3.10 版本加入。

statistics.correlation(x, y, /, *, method='linear')

返回两个输入的皮尔逊相关系数。皮尔逊相关系数 r 取值在 -1 到 +1 之间。它衡量线性关系的强度和方向。

如果 method 是“ranked”,则计算两个输入的斯皮尔曼等级相关系数。数据被替换为等级。相同的数值取平均等级。结果系数衡量单调关系的强度。

斯皮尔曼相关系数适用于序数数据或不符合皮尔逊相关系数线性比例要求的连续数据。

两个输入必须长度相同(不少于两个),并且不必是常数,否则会引发 StatisticsError

开普勒行星运动定律相关的示例

>>> # Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, and  Neptune
>>> orbital_period = [88, 225, 365, 687, 4331, 10_756, 30_687, 60_190]    # days
>>> dist_from_sun = [58, 108, 150, 228, 778, 1_400, 2_900, 4_500] # million km

>>> # Show that a perfect monotonic relationship exists
>>> correlation(orbital_period, dist_from_sun, method='ranked')
1.0

>>> # Observe that a linear relationship is imperfect
>>> round(correlation(orbital_period, dist_from_sun), 4)
0.9882

>>> # Demonstrate Kepler's third law: There is a linear correlation
>>> # between the square of the orbital period and the cube of the
>>> # distance from the sun.
>>> period_squared = [p * p for p in orbital_period]
>>> dist_cubed = [d * d * d for d in dist_from_sun]
>>> round(correlation(period_squared, dist_cubed), 4)
1.0

在 3.10 版本加入。

3.12 版本中的更改: 添加了对斯皮尔曼等级相关系数的支持。

statistics.linear_regression(x, y, /, *, proportional=False)

返回使用普通最小二乘法估计的简单线性回归参数的斜率和截距。简单线性回归用以下线性函数描述自变量 x 和因变量 y 之间的关系:

y = 斜率 * x + 截距 + 噪声

其中 slopeintercept 是估计的回归参数,noise 代表线性回归未解释的数据变异性(它等于因变量的预测值和实际值之间的差异)。

两个输入必须长度相同(不少于两个),且自变量 x 不能是常数;否则会引发 StatisticsError

例如,我们可以使用Monty Python 电影的上映日期来预测假设他们保持相同速度到 2019 年 Monty Python 电影的累计数量。

>>> year = [1971, 1975, 1979, 1982, 1983]
>>> films_total = [1, 2, 3, 4, 5]
>>> slope, intercept = linear_regression(year, films_total)
>>> round(slope * 2019 + intercept)
16

如果 proportional 为真,则假定自变量 x 和因变量 y 成正比。数据拟合通过原点的直线。由于 intercept 总是 0.0,因此底层线性函数简化为:

y = 斜率 * x + 噪声

继续 correlation() 的示例,我们来看看基于主行星的模型能多好地预测矮行星的轨道距离。

>>> model = linear_regression(period_squared, dist_cubed, proportional=True)
>>> slope = model.slope

>>> # Dwarf planets:   Pluto,  Eris,    Makemake, Haumea, Ceres
>>> orbital_periods = [90_560, 204_199, 111_845, 103_410, 1_680]  # days
>>> predicted_dist = [math.cbrt(slope * (p * p)) for p in orbital_periods]
>>> list(map(round, predicted_dist))
[5912, 10166, 6806, 6459, 414]

>>> [5_906, 10_152, 6_796, 6_450, 414]  # actual distance in million km
[5906, 10152, 6796, 6450, 414]

在 3.10 版本加入。

3.11 版本中的更改: 添加了对 proportional 的支持。

异常

定义了一个异常。

exception statistics.StatisticsError

统计相关异常的 ValueError 子类。

NormalDist 对象

NormalDist 是一个用于创建和操作随机变量正态分布的工具。它是一个将数据测量的均值和标准差视为单个实体的类。

正态分布源于中心极限定理,并在统计学中有广泛应用。

class statistics.NormalDist(mu=0.0, sigma=1.0)

返回一个新的 NormalDist 对象,其中 mu 表示算术平均值sigma 表示标准差

如果 sigma 为负,则引发 StatisticsError

mean

正态分布算术平均值的只读属性。

median

正态分布中位数的只读属性。

mode

正态分布众数的只读属性。

stdev

正态分布标准差的只读属性。

variance

正态分布方差的只读属性。等于标准差的平方。

classmethod from_samples(data)

创建一个正态分布实例,其 musigma 参数通过 fmean()stdev()data 估计。

data 可以是任何 可迭代对象,并且应该包含可以转换为 float 类型的值。如果 data 不包含至少两个元素,则引发 StatisticsError,因为估计中心值至少需要一个点,估计离散度至少需要两个点。

samples(n, *, seed=None)

为给定的均值和标准差生成 n 个随机样本。返回一个 list 浮点值。

如果给定 seed,则创建底层随机数生成器的新实例。这对于生成可重现的结果很有用,即使在多线程环境中也是如此。

3.13 版本中的更改。

切换到更快的算法。要重现以前版本的样本,请使用 random.seed()random.gauss()

pdf(x)

使用概率密度函数 (pdf),计算随机变量 X 接近给定值 x 的相对可能性。在数学上,它是当 dx 趋近于零时,比率 P(x <= X < x+dx) / dx 的极限。

相对可能性被计算为样本出现在狭窄范围内的概率除以该范围的宽度(因此称为“密度”)。由于可能性是相对于其他点而言的,因此其值可以大于 1.0

cdf(x)

使用累积分布函数 (cdf),计算随机变量 X 小于或等于 x 的概率。在数学上,它表示为 P(X <= x)

inv_cdf(p)

计算逆累积分布函数,也称为分位数函数百分点函数。在数学上,它表示为 x : P(X <= x) = p

找到随机变量 X 的值 x,使得变量小于或等于该值的概率等于给定概率 p

overlap(other)

衡量两个正态概率分布之间的一致性。返回一个介于 0.0 和 1.0 之间的值,表示两个概率密度函数的重叠面积

quantiles(n=4)

将正态分布分成 n 个等概率的连续区间。返回一个由 (n - 1) 个分隔这些区间的切点组成的列表。

n 设置为 4 表示四分位数(默认)。将 n 设置为 10 表示十分位数。将 n 设置为 100 表示百分位数,它提供 99 个切点,将正态分布分成 100 个大小相等的组。

zscore(x)

计算描述 x标准分数,即 x 偏离正态分布均值的标准差数量:(x - mean) / stdev

在 3.9 版本中新增。

NormalDist 的实例支持与常数的加法、减法、乘法和除法。这些操作用于平移和缩放。例如:

>>> temperature_february = NormalDist(5, 2.5)             # Celsius
>>> temperature_february * (9/5) + 32                     # Fahrenheit
NormalDist(mu=41.0, sigma=4.5)

不支持用常数除以 NormalDist 的实例,因为结果将不是正态分布。

由于正态分布源于独立变量的加性效应,因此可以加减两个独立正态分布的随机变量,这些变量表示为 NormalDist 的实例。例如:

>>> birth_weights = NormalDist.from_samples([2.5, 3.1, 2.1, 2.4, 2.7, 3.5])
>>> drug_effects = NormalDist(0.4, 0.15)
>>> combined = birth_weights + drug_effects
>>> round(combined.mean, 1)
3.1
>>> round(combined.stdev, 1)
0.5

在 3.8 版本加入。

示例与技巧

经典概率问题

NormalDist 轻松解决经典概率问题。

例如,给定SAT 考试的历史数据,显示分数呈正态分布,平均值为 1060,标准差为 195,计算分数在 1100 到 1200 之间的学生百分比,四舍五入到最接近的整数。

>>> sat = NormalDist(1060, 195)
>>> fraction = sat.cdf(1200 + 0.5) - sat.cdf(1100 - 0.5)
>>> round(fraction * 100.0, 1)
18.4

找出 SAT 分数的四分位数十分位数

>>> list(map(round, sat.quantiles()))
[928, 1060, 1192]
>>> list(map(round, sat.quantiles(n=10)))
[810, 896, 958, 1011, 1060, 1109, 1162, 1224, 1310]

蒙特卡罗模拟的输入

为了估计难以进行分析求解的模型的分布,NormalDist 可以为蒙特卡罗模拟生成输入样本。

>>> def model(x, y, z):
...     return (3*x + 7*x*y - 5*y) / (11 * z)
...
>>> n = 100_000
>>> X = NormalDist(10, 2.5).samples(n, seed=3652260728)
>>> Y = NormalDist(15, 1.75).samples(n, seed=4582495471)
>>> Z = NormalDist(50, 1.25).samples(n, seed=6582483453)
>>> quantiles(map(model, X, Y, Z))
[1.4591308524824727, 1.8035946855390597, 2.175091447274739]

近似二项式分布

当样本量很大且成功试验的概率接近 50% 时,正态分布可用于近似二项式分布

例如,一个开源会议有 750 名参会者,两个会议室的容量为 500 人。有一个关于 Python 的讲座,另一个关于 Ruby 的讲座。在之前的会议中,65% 的参会者更喜欢听 Python 讲座。假设人口偏好没有改变,Python 会议室保持在其容量限制内的概率是多少?

>>> n = 750             # Sample size
>>> p = 0.65            # Preference for Python
>>> q = 1.0 - p         # Preference for Ruby
>>> k = 500             # Room capacity

>>> # Approximation using the cumulative normal distribution
>>> from math import sqrt
>>> round(NormalDist(mu=n*p, sigma=sqrt(n*p*q)).cdf(k + 0.5), 4)
0.8402

>>> # Exact solution using the cumulative binomial distribution
>>> from math import comb, fsum
>>> round(fsum(comb(n, r) * p**r * q**(n-r) for r in range(k+1)), 4)
0.8402

>>> # Approximation using a simulation
>>> from random import seed, binomialvariate
>>> seed(8675309)
>>> mean(binomialvariate(n, p) <= k for i in range(10_000))
0.8406

朴素贝叶斯分类器

正态分布通常出现在机器学习问题中。

维基百科上有一个关于朴素贝叶斯分类器的很好的例子。挑战在于根据身高、体重和脚尺寸等正态分布特征的测量结果来预测一个人的性别。

我们得到了一个包含八个人测量结果的训练数据集。测量结果假定为正态分布,因此我们使用 NormalDist 总结数据。

>>> height_male = NormalDist.from_samples([6, 5.92, 5.58, 5.92])
>>> height_female = NormalDist.from_samples([5, 5.5, 5.42, 5.75])
>>> weight_male = NormalDist.from_samples([180, 190, 170, 165])
>>> weight_female = NormalDist.from_samples([100, 150, 130, 150])
>>> foot_size_male = NormalDist.from_samples([12, 11, 12, 10])
>>> foot_size_female = NormalDist.from_samples([6, 8, 7, 9])

接下来,我们遇到一个新的人,其特征测量结果已知,但性别未知。

>>> ht = 6.0        # height
>>> wt = 130        # weight
>>> fs = 8          # foot size

从男性或女性 50% 的先验概率开始,我们将后验概率计算为先验概率乘以给定性别的特征测量结果似然的乘积。

>>> prior_male = 0.5
>>> prior_female = 0.5
>>> posterior_male = (prior_male * height_male.pdf(ht) *
...                   weight_male.pdf(wt) * foot_size_male.pdf(fs))

>>> posterior_female = (prior_female * height_female.pdf(ht) *
...                     weight_female.pdf(wt) * foot_size_female.pdf(fs))

最终预测取最大的后验概率。这称为最大后验估计或 MAP。

>>> 'male' if posterior_male > posterior_female else 'female'
'female'