每对 numpy.array 的中点

新手上路,请多包涵

我有一个形式的数组:

 x = np.array([ 1230.,  1230.,  1227.,  1235.,  1217.,  1153.,  1170.])

我想生成另一个数组,其中的值是原始数组中每对值的平均值:

 xm = np.array([ 1230.,  1228.5,  1231.,  1226.,  1185.,  1161.5])

有人知道不使用循环最简单快捷的方法吗?

原文由 iury simoes-sousa 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 408
2 个回答

更短,更甜美:

 (x[1:] + x[:-1]) / 2

  • 这更快:
   >>> python -m timeit -s "import numpy; x = numpy.random.random(1000000)" "x[:-1] + numpy.diff(x)/2"
  100 loops, best of 3: 6.03 msec per loop

  >>> python -m timeit -s "import numpy; x = numpy.random.random(1000000)" "(x[1:] + x[:-1]) / 2"
  100 loops, best of 3: 4.07 msec per loop

  • 这是完全准确的:

考虑 x[1:] + x[:-1] 中的每个元素。所以考虑 x₀x₁ ,第一个和第二个元素。

x₀ + x₁ 根据 IEEE 计算至完美精度, 然后 四舍五入。因此,如果这就是所需要的,那将是正确的答案。

(x₀ + x₁) / 2 只是该值的一半。这几乎总是可以通过将指数减一来完成,除了两种情况:

  • x₀ + x₁ 溢出。这将导致无穷大(任一符号)。这不是想要的,所以计算会 出错

  • x₀ + x₁ 下溢。随着大小的 _减小_,四舍五入将是完美的,因此计算将是 正确 的。

在所有其他情况下,计算都是 正确 的。


现在考虑 x[:-1] + numpy.diff(x) / 2 。通过检查源,这直接评估为

x[:-1] + (x[1:] - x[:-1]) / 2

所以再次考虑 x₀x₁

x₁ - x₀ 许多值会出现严重的 _下溢_“问题”。这也会因大量取消而失去精度。不过,目前尚不清楚符号是否相同并不重要,因为错误会在加法时有效抵消。重要的是 _发生舍入_。

(x₁ - x₀) / 2 将同样进行舍入,但是 x₀ + (x₁ - x₀) / 2 涉及 另一次 舍入。这意味着错误 悄悄进入。证明:

 import numpy

wins = draws = losses = 0

for _ in range(100000):
    a = numpy.random.random()
    b = numpy.random.random() / 0.146

    x = (a+b)/2
    y = a + (b-a)/2

    error_mine   = (a-x) - (x-b)
    error_theirs = (a-y) - (y-b)

    if x != y:
        if abs(error_mine) < abs(error_theirs):
            wins += 1
        elif abs(error_mine) == abs(error_theirs):
            draws += 1
        else:
            losses += 1
    else:
        draws += 1

wins / 1000
#>>> 12.44

draws / 1000
#>>> 87.56

losses / 1000
#>>> 0.0

这表明,对于精心选择的常量 1.46diff 变体有 12-13% 的答案是错误的!不出所料,我的版本总是正确的。

现在考虑 _下溢_。尽管我的变体存在溢出问题,但这些问题比取消问题要小得多。从上面的逻辑来看,为什么双舍入是非常有问题的,应该很明显了。证明:

 ...
    a = numpy.random.random()
    b = -numpy.random.random()
...

wins / 1000
#>>> 25.149

draws / 1000
#>>> 74.851

losses / 1000
#>>> 0.0

是的,它有 25% 的错误率!

事实上,不需要太多修剪就可以达到 50%:

 ...
    a = numpy.random.random()
    b = -a + numpy.random.random()/256
...

wins / 1000
#>>> 49.188

draws / 1000
#>>> 50.812

losses / 1000
#>>> 0.0

好吧,还不错。我认为 _,只要符号相同_,它就只有 1 个最低有效位。


所以你有它。我的答案是最好的,除非你找到两个值的平均值,其总和超过 1.7976931348623157e+308 或小于 -1.7976931348623157e+308

原文由 Veedrac 发布,翻译遵循 CC BY-SA 3.0 许可协议

简短而甜美:

 x[:-1] + np.diff(x)/2

即取 x 除最后一个元素外的每个元素,加上它与后续元素之差的二分之一。

原文由 John Zwinck 发布,翻译遵循 CC BY-SA 3.0 许可协议

推荐问题