statistics — 数学统计函数

3.4 版本新增。

源代码: Lib/statistics.py


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

此模块的目的不是要与诸如 NumPySciPy 之类的第三方库竞争,也不是要与面向专业统计人员的专有全功能统计软件包(如 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的中心趋势或典型值。

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

不会为了获得精确结果而进行特殊处理。(但是,这在将来可能会改变。)

>>> 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*)、*logistic* 和 *sigmoid*。

仅对带宽内的样本点赋予权重的核包括 *rectangular* (*uniform*)、*triangular*、*parabolic* (*epanechnikov*)、*quartic* (*biweight*)、*triweight* 和 *cosine*。

如果 cumulative 为 true,将返回累积分布函数。

如果 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 估计,这些值可能会略有变化。种子可以是整数、浮点数、字符串或字节。

如果 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 为空,则会引发 StatisticsError 异常。data 可以是序列或可迭代对象。

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

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

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

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

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

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

statistics.median_low(data)

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

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

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

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

statistics.median_high(data)

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

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

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

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

statistics.median_grouped(data, interval=1.0)

估计围绕连续、固定宽度区间的中点分组或分箱的数值数据的中位数。

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

interval 是每个 bin 的宽度。

例如,人口统计信息可能已汇总为连续的十年年龄组,每个组由区间的 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() 函数会假设 30 多岁年龄组中的每个人都正好是 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 为空,则返回空列表。

>>> 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

支持小数和分数

>>> 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 使用任意值可能会导致无效或不可能的结果。

支持小数和分数。

>>> 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%。

方法设置为“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 + 截距 + 噪声

其中 斜率截距 是被估计的回归参数,而 噪声 表示线性回归未解释的数据变异性(它等于因变量的预测值和实际值之间的差值)。

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

例如,我们可以使用巨蟒剧团电影的上映日期来预测假设他们保持节奏,到 2019 年应该制作的巨蟒剧团电影的累积数量。

>>> 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 成正比。数据拟合到一条穿过原点的直线。由于 截距 将始终为 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)

使用 fmean()stdev()data 估计的 musigma 参数创建一个正态分布实例。

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

samples(n, *, seed=None)

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

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

在 3.13 版本中更改。

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

pdf(x)

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

相对可能性计算为在狭窄范围内出现样本的概率除以范围的宽度(因此称为“密度”)。由于可能性是相对于其他点的,因此其值可以大于 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 - 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'