Python的
statistics模块不是为速度而构建的,而是为了精度
在此模块的规格中,似乎
当处理大小相差悬殊的浮点时,内置总和可能会失去准确性。因此,上述幼稚的平均值未能通过“酷刑测试”
assert mean([1e30, 1, 3, -1e30]) == 1返回0而不是1,则纯计算误差为100%。
在均值内部使用math.fsum可以使其更准确地处理浮点数据,但它也具有将任何参数转换为浮点的副作用,即使在不必要时也是如此。例如,我们应该期望分数列表的平均值是分数,而不是浮点数。
相反,如果我们看一下
_sum()此模块中的实现,该方法的文档字符串的第一行似乎可以确认:
def _sum(data, start=0): """_sum(data [, start]) -> (type, sum, count) Return a high-precision sum of the given numeric data as a fraction, together with the type to be converted to and the count of items. [...] """
因此,是的,
statistics实现
sum,而不是对Python内置
sum()函数的简单单线调用,它本身需要大约20行,并且
for在其主体中有嵌套循环。
发生这种情况是因为
statistics._sum选择了保证它可能遇到的所有类型的数字(即使它们彼此之间差异很大)的最大精度,而不是简单地强调速度。
因此,内置
sum证明快一百倍似乎是正常的。它的精度要低得多,而恰恰是用奇异数字来称呼它的代价。
其他选择
如果需要在算法中优先考虑速度,则应该看一下Numpy,其算法在C中实现。
NumPy的平均值不如
statistics远景那么精确,但它(自2013年起)实现了基于成对求和的例程,该例程优于幼稚
sum/len(链接中的更多信息)。
然而…
import numpy as npimport statisticsnp_mean = np.mean([1e30, 1, 3, -1e30])statistics_mean = statistics.mean([1e30, 1, 3, -1e30])print('NumPy mean: {}'.format(np_mean))print('Statistics mean: {}'.format(statistics_mean))> NumPy mean: 0.0> Statistics mean: 1.0


