Numpy: reduceatコーナーケース(Trac#236)

作成日 2012年10月19日  ·  49コメント  ·  ソース: numpy/numpy

_unknownに割り当てられたtracユーザーmartin_wiechertによる2006-08-07の元のチケットhttp://projects.scipy.org/numpy/ticket/236。_

.reduceatは、繰り返されるインデックスを正しく処理しません。 インデックスが繰り返されると、操作の中立要素が返されます。 以下の例では、[1、10]ではなく[0、10]が想定されています。

In [1]:import numpy

In [2]:numpy.version.version
Out[2]:'1.0b1'

In [3]:a = numpy.arange (5)

In [4]:numpy.add.reduceat (a, (1,1))
Out[4]:array([ 1, 10])
01 - Enhancement 23 - Wish List numpy.core

最も参考になるコメント

reduceatの主な動機は、最高速度を得るためにreduceを超えるループを回避することです。 したがって、 reduce上のforループのラッパーがNumpyへの非常に便利な追加になるかどうかは完全にはわかりません。 それはreduceat主な目的に反します。

さらに、 reduceat存在とAPIのロジックは、 reduce超えるループの高速ベクトル化された置換として、クリーンで便利です。 非推奨にするのではなく、修正します。

reduceat速度に関して、簡単な例を考えてみましょう。ただし、 reduceatを使用する、自分のコードにある実際のケースと似ています。

n = 10000
arr = np.random.random(n)
inds = np.random.randint(0, n, n//10)
inds.sort()

%timeit out = np.add.reduceat(arr, inds)
10000 loops, best of 3: 42.1 µs per loop

%timeit out = piecewise_reduce(np.add, arr, inds)
100 loops, best of 3: 6.03 ms per loop

これは100倍以上の時差であり、 reduceat効率を維持することの重要性を示しています。

要約すると、新しい関数の導入よりもreduceat修正を優先します。

start_indicesend_indicesを使用することは、場合によっては便利ですが、冗長であることが多く、追加の可能性があると思いますが、現在のreduceat不整合に対する修正ではありません。動作。

全てのコメント49件

_ @teoliphantは2006-08-08_に書き込みました

残念ながら、おそらく、NumPyのreduceatメソッドは、このコーナーケースのNumericのreduceatメソッドの動作に従います。

インデックスが等しい場合、操作の「単位元」要素を返す機能はありません。 定義された動作は、スライスが空のシーケンスを返す場合、最初のインデックスで指定された要素を返すことです。 したがって、この場合のreduceatの文書化された実際の動作は、

[a [1]、add.reduce(a [1:])]

これは機能リクエストです。

_tracユーザーmartin_wiechertは2006-08-08_に書き込みました

チケット#835もご覧ください

マイルストーンは2007-05-12に@albertsによって1.1されまし

マイルストーンは2009-03-02に@cournapeによってUnscheduledされまし

これは#835と密接に関連していると思います。インデックスの1つがlen(a)場合、 reduceatはそのインデックスの要素を出力できません。これは、インデックスlen(a)が表示される場合に必要です。または、インデックスの最後で繰り返されます。

いくつかの解決策:

  • end - start == 0出力に値を設定しない、 reduceatオプション
  • end - start == 0である特定の固定値に出力を設定するオプション
  • ufunc()ように、 whereパラメータ。これufunc() 、どの出力を計算する必要があるかをマスクします。

この問題についてこれ以上考えたことはありますか? 出力をID値(存在する場合)に設定するオプションがあることに興味があります。ここで、end --start == 0です。

この長年の未解決の問題で提案されているように、 reduceat動作の変更を強く支持します。 この素晴らしいNumpyコンストラクトの有用性を妨げる明らかなバグまたは明らかな設計ミスのように見えます。

reduceatは、すべてのインデックスで一貫して動作する必要があります。 つまり、すべてのインデックスiについて、 ufunc.reduceat(a, indices)ufunc.reduce(a[indices[i]:indices[i+1]])を返す必要があります。

これは、 indices[i] == indices[i+1]場合にも当てはまります。 私は、この場合には、reduceatを返す必要がありますなぜ、どの賢明な理由で見ることができませんa[indices[i]]の代わりにufunc.reduce(a[indices[i]:indices[i+1]])

参照してくださいHEREによって同様のコメントパンダのクリエイターウェスマッキニーを

うわー、これは確かにひどくて壊れています。

メーリングリストで話し合う必要がありますが、少なくとも私は
次のリリースでその問題をFutureWarningにすることに完全に賛成
数リリース後に動作を修正します。 私たちは誰かが取る必要があります
その議論を開始し、パッチを書くことにつながる。 多分それはあなたですか?

協力的な対応をありがとう。 これが役に立ったら議論を始めることができますが、残念ながらCコードにパッチを当てることはできません。

np.maximumなどのIDのないufuncに対して何を意図していますか?

このような関数の場合、空の削減はエラーになるはずです。
.reduceat()の代わりに.reduce()を使用する場合。

実際、動作はufunc.reduce(a[indices[i]:indices[i+1]])との一貫性によって駆動される必要があります。これは、すべてのユーザーが期待することです。 したがって、これには新しい設計上の決定は必要ありません。 それは本当に私には長年のバグ修正のように見えます。 誰かが現在の一貫性のない行動を正当化できない限り。

@njsmithNumpyリストにサインアップできません。 https://mail.scipy.org/mailman/listinfo/numpy-discussionにアドレスを送信しましたが、「確認を要求するメール」が届きません。 サブスクライブするために特別な要件が必要かどうかわからない...

@divenex :スパムフォルダを確認しましたか? (私はいつもそれをするのを忘れています...)そうでなければ、何がうまくいかないのかわかりません。 「メールアドレスを持っている」以外に購読するための特別な要件は絶対にありません。 それでも機能しない場合は、声を上げて、関連するシステム管理者を追跡しようとします...壊れているかどうかを確認したいと思います。

バージョンreduceatと一致しているufunc.reduce(a[indices[i]:indices[i+1]])本当に、本当にいいだろう。 それはとても便利でしょう! 動作を選択するための引数または新しい関数( reduce_intervalsreduce_segments ?...?)は、後方の非互換性を壊すことを回避します。

np.ufunc.reduceat完全に非推奨にしたいと思うかもしれません- indices[i] > indices[i+1]場合を避けるために、開始インデックスと終了インデックスのセットを指定できる方が便利なようです。 また、 atという名前at 、通常存在するよりも

私が代替品として提案するのはnp.piecewise_reduce np.reducebins 、おそらく純粋なPythonであり、基本的に次のことを行います。

def reducebins(func, arr, start=None, stop=None, axis=-1, out=None):
    """
    Compute (in the 1d case) `out[i] = func.reduce(arr[start[i]:stop[i]])`

    If only `start` is specified, this computes the same reduce at `reduceat` did:

        `out[i]  = func.reduce(arr[start[i]:start[i+1]])`
        `out[-1] = func.reduce(arr[start[-1]:])`

    If only `stop` is specified, this computes:

        `out[0] = func.reduce(arr[:stop[0]])`
        `out[i] = func.reduce(arr[stop[i-1]:stop[i]])`

    """
    # convert to 1d arrays
    if start is not None:
        start = np.array(start, copy=False, ndmin=1, dtype=np.intp)
        assert start.ndim == 1
    if stop is not None:
        stop = np.array(stop, copy=False, ndmin=1, dtype=np.intp)
        assert stop.ndim == 1

    # default arguments that do useful things
    if start is None and stop is None:
        raise ValueError('At least one of start and stop must be specified')
    elif stop is None:
        # start only means reduce from one index to the next, and the last to the end
        stop = np.empty_like(start)
        stop[:-1] = start[1:]
        stop[-1] = arr.shape[axis]
    elif start is None:
        # stop only means reduce from the start to the first index, and one index to the next
        start = np.empty_like(stop)
        start[1:] = stop[:-1]
        start[0] = 0
    else:
        # TODO: possibly confusing?
        start, stop = np.broadcast_arrays(start, stop)

    # allocate output - not clear how to do this safely for subclasses
    if not out:
        sh = list(arr.shape)
        sh[axis] = len(stop)
        sh = tuple(sh)
        out = np.empty(shape=sh)

    # below assumes axis=0 for brevity here
    for i, (si, ei) in enumerate(zip(start, stop)):
        func.reduce(arr[si:ei,...], out=out[i, ...], axis=axis)
    return out

これには、次のような優れたプロパティがあります。

  • np.add.reduce(arr)np.piecewise_reduce(np.add, arr, 0, len(arr))と同じです
  • np.add.reduceat(arr, inds)np.piecewise_reduce(np.add, arr, inds)と同じです
  • np.add.accumulate(arr)np.piecewise_reduce(np.add, arr, 0, np.arange(len(arr)))と同じです

さて、これは__array_ufunc__機械を通過したいですか? 処理する必要があるもののほとんどは、すでにfunc.reduceカバーされているはずです。唯一の問題はnp.empty行です。これは、 np.concatenate共有する問題です。

これは、APIの観点からは私にとって素晴らしい解決策のように思えます。 reduceat 2セットのインデックスを指定できれば十分です。 実装の観点から? 現在のPyUFunc_Reduceatを変更して、2セットのindを持つことをサポートすることは、それが利益をもたらすのであれば、それほど難しくありません。 アキュムレートのようなユースケースを効率的にサポートすることの利点を実際に見れば、それも難しくありません。

マーテンは〜1からの同様の議論でこれに似た何かを提案しました
1年前ですが、彼は「step」オプションを追加する可能性についても言及しました。

http://numpy-discussion.10968.n7.nabble.com/Behavior-of-reduceat-td42667.html

あなたの提案から私が好きなもの(誰かが数えているなら+1):

  • 既存の関数を回収しようとするのではなく、新しい関数を作成する
    1。
  • 開始インデックスと終了インデックスの引数を、ではなく特定にする
    多次元配列から魔法のようにそれらを理解します。
  • Noneインデックスのデフォルトは非常に適切です。

この新しい機能について真剣に考えることが重要だと思うことは次のとおりです。

  • 'step'をオプションにする必要がありますか? (私はそう言うでしょう)
  • インデックス配列がブロードキャストすることは理にかなっていますか、それとも
    1Dになりますか?
  • これはnp関数ですか、それともufuncメソッドですか? (私はそれが好きだと思います
    方法として)

そして、自転車の脱落部門から、私はもっと好きです:

  • もっと覚えやすい名前を付けてください。しかし、私には提案がありません。
  • 'start'と 'stop'(そしてそれを実行することにした場合は 'step')を使用します
    np.arangeおよびPythonのスライスとの整合性。
  • kwarg名から_indicesを削除します。

ハイメ

13:47の木、2017年4月13日には、エリックWieserの[email protected]
書きました:

私はおそらくnp.ufunc.reduceatを完全に非推奨にしたいと思うでしょう-それは
開始インデックスと終了インデックスのセットを指定できると、より便利に思えます。
インデックス[i]>インデックス[i + 1]の場合は避けてください。 また、nameatsuggests
通常存在するよりもはるかに大きな類似性toat`

代わりに提案するのはnp.piecewise_reduceで、これは基本的に
します:

def piecewise_reduce(func、arr、start_indices = None、end_indices = None、axis = -1、out = None):
start_indicesがNoneで、end_indicesがNoneの場合:
start_indices = np.array([0]、dtype = np.intp)
end_indices = np.array(arr.shape [axis]、dtype = np.intp)
elif end_indicesはNoneです:
end_indices = np.empty_like(start_indices)
end_indices [:-1] = start_indices [1:]
end_indices [-1] = arr.shape [axis]
elif start_indicesはNoneです:
start_indices = np.empty_like(end_indices)
start_indices [1:] = end_indices
end_indices [0] = 0
そうしないと:
len(start_indices)== len(end_indices)をアサートします

if not out:
    sh = list(arr.shape)
    sh[axis] = len(end_indices)
    out = np.empty(shape=sh)

# below assumes axis=0 for brevity here
for i, (si, ei) in enumerate(zip(start_indices, end_indices)):
    func.reduce(arr[si:ei,...], out=alloc[i, ...], axis=axis)
return out

これには、次のような優れたプロパティがあります。

  • np.ufunc.reduceは、np.piecewise_reduce(func、arr、0、
    len(arr))
  • np.ufunc.accumulateは `np.piecewise_reduce(func、arr、
    np.zeros(len(arr))、np.arange(len(arr)))

さて、これは__array_ufunc__機構を通過したいですか? たいていの
処理する必要があるものは、func.reduceですでにカバーされている必要があります-
唯一の問題はnp.empty行です。これは、np.concatenateの問題です。
株式。


このスレッドにサブスクライブしているため、これを受け取っています。
このメールに直接返信し、GitHubで表示してください
https://github.com/numpy/numpy/issues/834#issuecomment-293867746 、またはミュート
スレッド
https://github.com/notifications/unsubscribe-auth/ADMGdtjSCodONyu6gCpwofdBaJMCIKa-ks5rvgtrgaJpZM4ANcqc

-
(__ /)
(Oo)
(> <)Este esConejo。 Copia a Conejoentufirmayayúdaleensusplanes
dedominaciónmundial。

「開始」と「停止」を使用する

完了

'step'をオプションにする必要があります

かなり狭いユースケースのようです

インデックス配列をブロードキャストすることは理にかなっていますか、それとも1Dである必要がありますか

更新しました。 > 1dは明らかに悪いですが、累積のような場合には、0dとブロードキャストを許可する必要があると思います。

これはnp関数ですか、それともufuncメソッドですか? (私はそれが好きだと思います
方法として)

すべてのufuncメソッドは、 __array_ufunc__が処理するもう1つのものです。

reduceatの主な動機は、最高速度を得るためにreduceを超えるループを回避することです。 したがって、 reduce上のforループのラッパーがNumpyへの非常に便利な追加になるかどうかは完全にはわかりません。 それはreduceat主な目的に反します。

さらに、 reduceat存在とAPIのロジックは、 reduce超えるループの高速ベクトル化された置換として、クリーンで便利です。 非推奨にするのではなく、修正します。

reduceat速度に関して、簡単な例を考えてみましょう。ただし、 reduceatを使用する、自分のコードにある実際のケースと似ています。

n = 10000
arr = np.random.random(n)
inds = np.random.randint(0, n, n//10)
inds.sort()

%timeit out = np.add.reduceat(arr, inds)
10000 loops, best of 3: 42.1 µs per loop

%timeit out = piecewise_reduce(np.add, arr, inds)
100 loops, best of 3: 6.03 ms per loop

これは100倍以上の時差であり、 reduceat効率を維持することの重要性を示しています。

要約すると、新しい関数の導入よりもreduceat修正を優先します。

start_indicesend_indicesを使用することは、場合によっては便利ですが、冗長であることが多く、追加の可能性があると思いますが、現在のreduceat不整合に対する修正ではありません。動作。

開始インデックスと停止インデックスを異なる配列から取得できるようにすることはないと思います
Cで実装すると、効率に大きな違いが生じます。

午前23時40分に2017年4月13日、divenex [email protected]書きました:

reduceatの主な動機は、reduceのループを回避することです。
最大速度。 したがって、forループのラッパーが完全にわからない
reduceはNumpyへの非常に便利な追加です。 それは反対するだろう
主な目的を減らす。

さらに、高速ベクトル化された、reduceatの存在とAPIのロジック
ループオーバーリデュースの交換は、クリーンで便利です。 わたしは・・・しないだろう
非推奨になりますが、修正してください。

減速速度については、簡単な例を考えてみましょう。
私がreduceatを使用する、私自身のコードにあるいくつかの実際のケース:

n = 10000
arr = np.random.random(n)
inds = np.random.randint(0、n、n // 10)
inds.sort()
%timeit out = np.add.reduceat(arr、inds)10000ループ、ベスト3:ループあたり42.1 µs
%timeit out = piecewise_reduce(np.add、arr、inds)100ループ、ベスト3:ループあたり6.03ミリ秒

これは100倍以上の時差であり、重要性を示しています
削減効率を維持する。

要約すると、私は新しいものを導入するよりもreduceatを修正することを優先します
関数。

start_indicesとend_indicesがあると、場合によっては便利ですが、
多くの場合冗長であり、追加の可能性があると思いますが、修正ではありません
現在のreduceatの一貫性のない動作について。


あなたがコメントしたのであなたはこれを受け取っています。
このメールに直接返信し、GitHubで表示してください
https://github.com/numpy/numpy/issues/834#issuecomment-293898215 、またはミュート
スレッド
https://github.com/notifications/unsubscribe-auth/AAEz6xPex0fo2y_MqVHbNP5YNkJ0CBJrks5rviW-gaJpZM4ANcqc

これは100倍以上の時差であり、reduceat効率を維持することの重要性を示しています。

そのおかげで- reduce呼び出しの最初の段階に関連するオーバーヘッドを過小評価したと思います(これはreduceat回だけ発生します)。

無料の関数に反対する議論ではありませんが、純粋なPythonでそれを実装することに反対する議論は確かです

ただし、現在のreduceatの一貫性のない動作の修正としてではありません。

問題は、長い間存在していたコードの動作を変更するのが難しいことです。


別の可能な拡張: indices[i] > indices[j]場合、逆を計算します。

    for i, (si, ei) in enumerate(zip(start, stop)):
        if si >= ei:
            func.reduce(arr[si:ei,...], out=out[i, ...], axis=axis)
        else:
            func.reduce(arr[ei:si,...], out=out[i, ...], axis=axis)
            func.inverse(func.identity, out[i, ...], out=out[i, ...])

ここで、 np.add.inverse = np.subtractnp.multiply.inverse = np.true_divide 。 これにより、次のような優れたプロパティが得られます。

func.reduce(func.reduceat(x, inds_from_0)) == func.reduce(x))

例えば

a = [1, 2, 3, 4]
inds = [0, 3, 1]
result = np.add.reduceat(a, inds) # [6, -5, 9] == [(1 + 2 + 3), -(3 + 2), (2 + 3 + 4)]

問題は、長い間存在していたコードの動作を変更するのが難しいことです。

これが部分的に、電子メールスレッドで、追加の次元が2または3であるインデックスの2次元配列に特別な意味を与えることを提案した理由です。これは(事実上)スライスのスタックとして解釈されます。 しかし、これもやや面倒で、もちろんreduce_by_sliceslicereduce 、またはreducesliceメソッドを使用することもできます。

ps多くのufuncで機能するものはすべてメソッドである必要があると思います。そうすれば、 __array_ufunc__を通過して、オーバーライドできます。

実際、私が考える別の提案ははるかに優れています: reduceatを回収するのではなく、 slice引数(またはstartstopstep )からufunc.reduce !? @ eric-wieserが指摘したように、このような実装は、 reduceat完全に非推奨にすることができることを意味します。

add.reduce(array, slice=slice(indices[:-1], indices[1:])

(ここで、空のスライスに期待される動作と一致させることができます)

ここでは、スライスが0-dの場合はブロードキャストし、軸のタプルが使用されている場合はスライスのタプルを渡すことを検討することもできます。

編集:上記のslice(indices[:-1], indices[1:])を作成して、スライスのタプルへの拡張を可能にしました( sliceは任意のデータを保持できるため、これは正常に機能します)。

それでもreduceat修正を見つけて、最も論理的な設計ソリューションであるreduce適切な100%ベクトル化バージョンにします。 または、コードの破損を回避するために(ただし、以下を参照)、 reducebinsような名前の同等のメソッドを作成できます。これは、単にreduceat修正バージョンです。 実際、私は@ eric-wieserに同意します。 reduceatの名前は、 at関数への接続を実際よりも多く伝えます。

コードを壊さない必要があることは理解しています。 しかし、論理的に意味がないことを考えると、多くのコードが古い動作に依存していることを想像するのは難しいと言わざるを得ません。私はそれを単に長年のバグと呼んでいます。 reduceatを使用するコードは、 reduceatからの意味のない結果を回避するために、 indicesが複製されていないことを確認するか、 out[:-1] *= np.diff(indices) > 0を使用した場合のように出力を修正することを期待します。

@mhvk sliceソリューションについては、 slice構造の非標準的な使用法が導入されているため、完全には確信していません。 さらに、それはreduceの現在の設計アイデアと矛盾します。これは、_ "1つの軸に沿ってufuncを適用することにより、aの次元を1つ減らす" _です。

また、 start endインデックスとreduceatメソッドの優れた設計ロジックは概念的にnp.histogramに似ており、 binsは_ "ビンのエッジを定義します" _は次のように置き換えられます。 indices 。これもビンのエッジを表しますが、値ではなくインデックススペースにあります。 また、 reduceatは、ビンのエッジの各ペア内に含まれる要素に関数を適用します。 ヒストグラムは非常に人気のある構成ですが、Numpyには、左端と右端の2つのベクトルを渡すオプションは必要ありません。 同じ理由で、 reduceatまたはその代替品の両方のエッジが強く必要であるとは思えません。

reduceatの主な動機は、最大速度のためにreduceのループを回避することです。 したがって、forループオーバーリデュースのラッパーがNumpyへの非常に便利な追加になるかどうかは完全にはわかりません。 それはreduceatの主な目的に反するでしょう。

ここで@divenexに同意します。 reduceatではインデックスを並べ替えてオーバーラップする必要があるという事実は、データの1回のパスでループをキャッシュ効率的に計算できるようにするための合理的な制約です。 重複するビンが必要な場合は、ほぼ確実に、目的の操作を計算するためのより良い方法があります(たとえば、ローリングウィンドウの集計)。

また、最もクリーンな解決策は、固定APIを使用してreducebinsなどの新しいメソッドを定義すること(およびreduceatを非推奨にすること)であり、それをreduceに絞り込もうとしないことにも同意します。すでに何か違うことをしている

皆さんこんにちは、

これはバグだという議論をつまみたいと思います。 これは、 docstringからの文書化された動作です:

For i in ``range(len(indices))``, `reduceat` computes
``ufunc.reduce(a[indices[i]:indices[i+1]])``, which becomes the i-th
generalized "row" parallel to `axis` in the final result (i.e., in a
2-D array, for example, if `axis = 0`, it becomes the i-th row, but if
`axis = 1`, it becomes the i-th column).  There are three exceptions to this:

* when ``i = len(indices) - 1`` (so for the last index),
  ``indices[i+1] = a.shape[axis]``.
* if ``indices[i] >= indices[i + 1]``, the i-th generalized "row" is
  simply ``a[indices[i]]``.
* if ``indices[i] >= len(a)`` or ``indices[i] < 0``, an error is raised.

そのため、 reduceat動作を変更しようとする試みには反対です。

githubをすばやく検索すると、この関数のさまざまな使用法がわかります。 ここにいる誰もが、厳密に増加するインデックスのみを使用していると確信していますか?

新しい関数の動作に関しては、個別の開始/停止配列がないと、機能が大幅に妨げられると私は主張します。 定期的に配列されていない重複するウィンドウの値を測定したい場合が多くあります(したがって、ローリングウィンドウは機能しません)。 たとえば、いくつかの独立した方法によって決定された関心領域。 また、 @ divenexは、Pythonの反復によるパフォーマンスの違いが

定期的に配列されていない重複するウィンドウの値を測定したい場合が多くあります(したがって、ローリングウィンドウは機能しません)。

はい。ただし、 reduceat実装されているような単純なループは使用したくないでしょう。 何らかの方法で中間結果を格納する独自のローリングウィンドウ計算を実装して、データの1回の線形パスで実行できるようにする必要があります。 しかし今、私たちはreduceatよりもはるかに複雑なアルゴリズムについて話している。

@shoyer一部のROIのみが重複している場合を想像できます。 そのような場合、カスタマイズされたアルゴリズムを書くことは非常にやり過ぎでしょう。 私たちの主なユーザーベースは科学者であることを忘れないでください。科学者は通常、時間が貧弱で、絶対的な最適ではなく、「十分に優れた」ソリューションを必要としています。 np.reduceatの複雑さに関連する定数係数が低いということは、純粋なPythonコードでより良いソリューションを得るのが難しいか不可能であることを意味します。ほとんどの場合、ユーザーが喜んで書くのはコードだけです。

@jni確かに、任意の開始と停止を持つグループに減らすと便利です。 しかし、それは私にはスコープの大幅な拡大のように感じ、 reduceat代わりではなく、別の方法に適したものです(これは、削除しない場合でも、確実に非推奨にします)。

任意の開始と停止を持つグループに減らすことが役立つ場合があります。 しかし、それは私にとって範囲の大幅な拡大のように感じます

これは私には非常に些細なことのようです。 現在、基本的にind1 = indices[i], ind2 = indices[i + 1]を実行するコードがあります。 同じ配列ではなく2つの異なる配列を使用するように変更するのは、ごくわずかな作業です。

そして、連続した範囲を渡されたときのシングルパスの動作は、現在とほぼ同じ速度である必要があります-唯一のオーバーヘッドは、nditerへのもう1つの引数です

これは私には非常に些細なことのようです。

丁度。 さらに、これはユーザーがreduceat (他のすべてのインデックスを使用して)持つ機能ですが、オーバーラップをサポートしない新しい関数では失われます。

さらに、2つのインデックス形式は、古い(奇妙な)動作をエミュレートできます。

def reduceat(func, arr, inds):
    deprecation_warning()
    start = inds
    stops = zeros(inds.shape)
    stops[:-1] = start[1:]
    stops[-1] = len(arr)
    np.add(stops, 1, where=ends == starts, out=stops)  # reintroduce the "bug" that we would have to keep
    return reducebins(func, arr, starts, stops)

つまり、2つの非常に類似した実装を維持する必要はありません

新しいreducebins startsインデックスとstopsインデックスには強く反対していませんが、両方が必要な明確な例はまだわかりません。 binsエッジと終了エッジを追加して、 np.histogram一般化するように感じます...

最終的には、主な使用法が影響を受けず、単一のインデックス配列を使用して速度を犠牲にすることなくreducebins(arr, indices)を呼び出すことができる限り、これは問題ありません。

もちろん、重複しないビンを操作する必要がある状況はたくさんありますが、この場合、通常、ビンはエッジのペアだけで定義されないことが予想されます。 シナリオのこの種のために利用可能な機能は、scipyのダウンロードのあるndimage.labeled_comprehension 、など関連機能ndimage.sumようにと。

しかし、これはreducebinsの範囲とはかなり異なるようです。

だから、何のための自然な使用方法の場合であるstartsstopsreducebins

では、reducebinsでの開始と停止の自然な使用例は何でしょうか?

他の方法で達成できますが、長さk移動平均はreducebins(np,add, arr, arange(n-k), k + arange(n-k))ます。 インデックスを割り当てるコストを無視すると、パフォーマンスはas_stridedアプローチに匹敵すると思います。

ユニークなことに、 reducebinsは、さまざまな期間の移動平均を許可します。これは、 as_stridedでは不可能です。

もう1つのユースケース-1つの引数の形式で終了または開始を含めることを明確にします。

例えば:

a = np.arange(10)
reducebins(np.add, start=[2, 4, 6]) == [2 + 3, 4 + 5, 6 + 7 + 8 + 9]  # what `reduceat` does
reducebins(np.add, stop=[2, 4, 6])  == [0 + 1, 2 + 3, 4 + 5]          # also useful

もう1つのユースケース-1つの引数の形式で終了または開始を含めることを明確にします。

私はこれをよく理解していません。 ここに入力テンソルを含めることはできますか? また、 start / stopのデフォルト値は何ですか?

とにかく、私は別々の議論に強く反対していませんが、それは置き換えのようにきれいではありません。 「reduceatを使用せず、代わりにreducebinsを使用してください」と言いたいのですが、インターフェイスの外観が異なると、(少し)難しくなります。

実際、開始/停止オプションでさえ、空のスライスのユースケースをカバーしていないことに気づきました。これは、過去に私にとって有用でした。私のプロパティ/ラベルがCSRスパース行列の行に対応する場合、 indptrの値を使用して削減を行います。 reduceatを使用すると、空の行を無視できます。 交換には追加の簿記が必要になります。 したがって、どのような代替品を思いついたとしても、 reduceatを残してください。

In [2]: A = np.random.random((4000, 4000))
In [3]: B = sparse.csr_matrix((A > 0.8) * A)
In [9]: %timeit np.add.reduceat(B.data, B.indptr[:-1]) * (np.diff(B.indptr) > 1)
1000 loops, best of 3: 1.81 ms per loop
In [12]: %timeit B.sum(axis=1).A
100 loops, best of 3: 1.95 ms per loop
In [16]: %timeit np.maximum.reduceat(B.data, B.indptr[:-1]) * (np.diff(B.indptr) > 0)
1000 loops, best of 3: 1.8 ms per loop
In [20]: %timeit B.max(axis=1).A
100 loops, best of 3: 2.12 ms per loop

ちなみに、空のシーケンスの難問は、 Pythonと同じ方法で解決できます。つまり、初期値を指定することです。 これは、 indicesと同じ形状のスカラーまたは配列である可能性があります。

はい、私は最初の焦点が空のスライスを解決することにある必要があることに同意します
場合。 start = endの場合、出力を設定する方法があります。
要素をIDに変換するか、出力要素を変更しないようにします。
指定された配列。 電流の問題はそれが上書きされることです
無関係なデータで

私は彼の最後のコメントについて@shoyerと完全に一緒です。

out=ufunc.reducebins(a, inds)を最後を除くすべてのiに対してout[i]=ufunc.reduce(a[inds[i]:inds[i+1]]) out=ufunc.reducebins(a, inds)として定義し、 reduceatを非推奨にしましょう。

startsおよびendsインデックスの現在のユースケースは、 as_stridedまたは畳み込みなどの代替関数を使用すると、より自然に、おそらくより効率的に実装されるようです。

@shoyer

私はこれをよく理解していません。 ここに入力テンソルを含めることはできますか? また、開始/停止のデフォルト値は何ですか?

入力で更新されました。 デフォルト値については、これたコメントreduce_binsの実装を参照してください。 そこにもdocstringを追加しました。 その実装は機能が完全ですが、遅いです(Pythonであるため)。

しかし、インターフェースが異なって見える場合、それは(少し)難しくなります。

start引数が

np.add.reduce_at(arr, inds)
reduce_bins(np.add, arr, inds)
reduce_bins(np.add, arr, start=inds)

(メソッド/関数の区別は私があまり気にしないものであり、Pythonのプロトタイプとして新しいufuncメソッドを定義することはできません!)


@jni

実際、開始/停止オプションでさえ、空のスライスのユースケースをカバーしていないことに気づきました。これは、過去に私にとって有用だったものです。

あなたは間違っています、それはそうです- ufunc.reduceatすでにしているのとまったく同じ方法で。 start[i] == end[i]を渡すだけでも可能です。

空のシーケンスの難問は、初期値を指定することで解決できます。

はい、これについてはすでに説明しました。 ufunc.reduceは、 ufunc.identity入力することですでにこれを行っています。 これは、特に#8952がマージされている場合、既存のufunc.reduecatに追加するのは難しくありません。 しかし、あなたが自分で言ったように、現在の動作は_文書化されている_ので、おそらくそれを変更するべきではありません。


@divenex

最後を除くすべてのiについて、out = ufunc.reducebins(a、inds)をout [i] = ufunc.reduce(a [inds [i]:inds [i + 1]])として簡単に定義しましょう。

だからlen(out) == len(inds) - 1 ? これはreduceatの現在の動作とは異なるため、切り替えに関する@shoyerの議論はここでより強力になります


すべて:このディスカッションが読みづらくなっていたため、以前のコメントを確認し、引用されたメールの返信を削除しました

@ eric-wieserの良い点。 上記の文では、最後のインデックスでは、 reducebinsの動作が現在のreduceatとは異なることを意味しました。 ただし、その場合、最後の値は正式には意味をなさないため、値がどうあるべきかわかりません。

互換性の懸念を無視すると、 np.diff(a)サイズがa.size-1np.histogram(a, bins)とまったく同じ理由で、 reducebins (1D)の出力のサイズはinds.size-1である必要があります。 np.histogram(a, bins)サイズはbins.size-1です。 ただし、これはreduceatドロップイン交換を希望することに反します。

a.size-1が正しい答えであるという説得力のある議論はないと思います-インデックス0および/またはインデックスnを含めることもかなり合理的な動作のようです。 状況によってはどれも便利に思えますが、交換を減らすことは非常に重要だと思います。

ここに隠れているstop / startについての別の議論もあります-それはあなたがそれを維持しながら非常に少ないコストでそれを望むならdiffような振る舞いを構築することを可能にしますreduceat動作:

a = np.arange(10)
inds = [2, 4, 6]
reduce_bins(a, start=inds[:-1], stop=inds[1:])  #  [2 + 3, 4 + 5]

# or less efficiently:
reduce_at(a, inds)[:-1}
reduce_bins(a, start=inds)[:-1]
reduce_bins(a, stop=inds)[1:]

@ eric-wieser必須のstart stop引数とstartだけを提供することが、私が推測したであろうout[i] = func.reduce(arr[start[i]:])ではなくout[i] = func.reduce(arr[start[i]:start[i+1]])意味することは明らかではありません。

以下のための私の好みAPI reducebins似ているreduceatけどに注意混乱"例外"なしドキュメンテーション文字列。 つまり、ちょうど:

i in range(len(indices))場合、reduceatはufunc.reduce(a[indices[i]:indices[i+1]])計算します。これは、最終結果の軸に平行なi番目の一般化された「行」になります(つまり、2次元配列では、たとえば、axis = 0の場合、i番目の行になりますが、axis = 1の場合、i番目の列になります)。

負でないインデックス( 0 <= indices[i] <= a.shape[axis] )を必要とする3番目の「例外」にどちらの方法でも進むことができます。これは、例外というよりはサニティチェックのように見えます。 しかし、おそらくそれも可能です。負のインデックスが誰かにとってどのように役立つかがわかります。そのようなインデックスを正規化するための計算を行うのは難しくありません。

最後にインデックスを自動的に追加しないということは、 np.histogram結果のように、結果の長さがlen(a)-1であることを意味します。

@jniスパース行列で見つかった配列から実際に計算したいものの例を教えてください。 できれば、乱数を使用せず、自己完結型の具体的な例を使用します(scipy.sparseに依存しません)。

開始のみを提供することが、out [i] = func.reduce(arr [start [i])ではな​​くout [i] = func.reduce(arr [start [i]:start [i + 1]])を意味することは明らかではありません。 :])、それは私が推測したものです。

私が目指していたのは、「各ビンはこれらの位置から始まる」ということであり、特に明記されていない限り、すべてのビンが連続していることを意味します。 おそらく、もっと完全なdocstringをドラフトしてみるべきでしょう。 どちらの引数も渡さないという強い議論が見られると思うので、それを提案関数から削除します。

非負のインデックスが必要です(0 <=インデックス[i] <a.shape [軸])

ここにもバグがあることに注意してください(#835)-これらはスライスであるため、上限は包括的である必要があります。

ここにもバグがあることに注意してください。これらはスライスであるため、上限は包括的である必要があります。

修正しました、ありがとう。

reduceat関数自体にはありません、あなたはしていません;)

:\doc\neps\groupby_additions.rstは、 reduceby関数の(IMO劣った)提案が含まれていることがわかりました。

このページは役に立ちましたか?
0 / 5 - 0 評価