为什么scipy.stats.nanmean会给出numpy.nansum不同的结果?

时间:2013-01-23 08:42:15

标签: python numpy floating-point scipy floating-point-precision

>>> import numpy as np
>>> from scipy import stats
>>> a = np.r_[1., 2., np.nan, 4., 5.]
>>> stats.nanmean(a)
2.9999999999999996
>>> np.nansum(a)/np.sum(~np.isnan(a))
3.0

我知道浮点表示的局限性。只是好奇为什么更笨拙的表达似乎会给出“更好”的结果。

3 个答案:

答案 0 :(得分:8)

首先,这里是scipy.nanmean(),以便我们知道我们要比较的内容:

def nanmean(x, axis=0):
    x, axis = _chk_asarray(x,axis)
    x = x.copy()
    Norig = x.shape[axis]
    factor = 1.0-np.sum(np.isnan(x),axis)*1.0/Norig

    x[np.isnan(x)] = 0
    return np.mean(x,axis)/factor

数学上,这两种方法是等价的。在数字上,它们是不同的。

您的方法涉及单一划分,并且恰好发生:

  • 分子(1. + 2. + 4. + 5.)可以完全表示为float;和
  • 分母(4.)是2的幂。

这意味着除法的结果是精确的3.

stats.nanmean()首先计算[1., 2., 0., 4., 5.]的均值,然后将其调整为NaNs。碰巧的是,这个意思(2.4)不能完全表示为float,所以从这一点来看计算是不精确的。

我没有多想过,但是有可能构建一个角色可以反转的例子,而stats.nanmean()会给出比其他方法更准确的结果。

让我感到惊讶的是,stats.nanmean()并不是简单地执行以下操作:

In [6]: np.mean(np.ma.MaskedArray(a, np.isnan(a)))
Out[6]: 3.0

在我看来,这对目前的做法来说是一种更好的方法。

答案 1 :(得分:2)

答案在stats.nanmean

的代码中
x, axis = _chk_asarray(x,axis)
x = x.copy()
Norig = x.shape[axis]
factor = 1.0-np.sum(np.isnan(x),axis)*1.0/Norig
x[np.isnan(x)] = 0
return np.mean(x,axis)/factor

我认为它与1.0 - np.sum有关,这是总和的减法。

答案 2 :(得分:1)

正如@eumiro所提到的那样,stats.nanmean以一种不同于你所做的直接单行方式的圆周方式计算平均值

来自相同的参考代码,

np.sum(np.isnan(x),axis)返回numpy.int32,当乘以* 1.0时,得到一个浮点近似值,而不是当结果为整数时得到的值,导致差值结果

>>> numpy.int32(1)*1.0/5
0.20000000000000001
>>> int(numpy.int32(1))*1.0/5
0.2
>>> type(np.sum(np.isnan(x),axis))
<type 'numpy.int32'>