web-dev-qa-db-ja.com

numpy.arrayの各ペアの中点

次の形式の配列があります。

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

そして、値が元の配列内の値の各ペアの平均である別の配列を作成したいと思います。

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

誰かがループを使用せずにそれを行う最も簡単で速い方法を知っていますか?

23

さらに短く、少し甘い:

_(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]_の各要素を検討します。最初の要素と2番目の要素である_x₀_と_x₁_を検討してください。

    IEEEに従って、_x₀ + x₁_は完全な精度で計算され、、次に丸められます。したがって、それだけで十分だったとしたら、それが正解でしょう。

    _(x₀ + x₁) / 2_はその値の半分です。これは、次の2つの場合を除いて、ほとんどの場合、指数を1減らすことで実行できます。

    • _x₀ + x₁_オーバーフロー。これにより、無限大(どちらかの符号)になります。それは望んでいることではないので、計算はwrongになります。

    • _x₀ + x₁_アンダーフロー。サイズが縮小であるため、丸めは完璧になり、計算は正しいになります。

    他のすべての場合では、計算は正しいになります。


    次に、x[:-1] + numpy.diff(x) / 2について考えます。これは、ソースの検査により、直接評価されます

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

    そして、もう一度_x₀_と_x₁_を検討してください。

    _x₁ - x₀_には、多くの値に対してunderflowを伴う深刻な「問題」があります。これも大きなキャンセルにより精度を失います。エラーが追加で効果的にキャンセルされるため、これが兆候が同じであるかどうかが問題ではないことはすぐには明らかではありません。重要なのは、丸めが発生することです。

    _(x₁ - x₀) / 2_も同様に丸められますが、x₀ + (x₁ - x₀) / 2another丸めを含みます。これは、エラーが忍び寄ることを意味します。証明:

    _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.46_の定数を注意深く選択した場合、diffバリアントでは12〜13%の回答が正しくないことを示しています。予想通り、私のバージョンは常に正しいです。

    underflowを考えてみましょう。私の亜種にはオーバーフローの問題がありますが、これらはキャンセルの問題ほど大した問題ではありません。上記のロジックからの二重丸めが非常に問題である理由は明らかです。証明:

    _...
        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_より小さい2つの値の平均を見つけない限り、私の答えが最良です。

52
Veedrac

短くて甘い:

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

つまり、最後の要素を除くxの各要素を取得し、それと後続の要素との差の半分を追加します。

8
John Zwinck

これを試して:

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

それはかなり簡単で、高速でなければなりません。

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

>>> (x+np.concatenate((x[1:], np.array([0]))))/2
array([ 1230. ,  1228.5,  1231. ,  1226. ,  1185. ,  1161.5,   585. ])

必要に応じて、最後の要素を削除できます

0
Pavel

私はこの操作を多次元配列にまとめて使用するので、私のソリューションを投稿します(np.diff()のソースコードに触発されます)

def zcen(a, axis=0):
    a = np.asarray(a)
    nd = a.ndim
    slice1 = [slice(None)]*nd
    slice2 = [slice(None)]*nd
    slice1[axis] = slice(1, None)
    slice2[axis] = slice(None, -1)
    return (a[slice1]+a[slice2])/2

>>> a = [[1, 2, 3, 4, 5], [10, 20, 30, 40, 50]]
>>> zcen(a)
array([[  5.5,  11. ,  16.5,  22. ,  27.5]])
>>> zcen(a, axis=1)
array([[  1.5,   2.5,   3.5,   4.5],
       [ 15. ,  25. ,  35. ,  45. ]])
0
Ben