Numpy: 一意のNaNエントリ(Trac#1514)

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

_unknownに割り当てられたtracユーザーrspringuelによる2010-06-18の元のチケットhttp://projects.scipy.org/numpy/ticket/1514。_

uniqueが複数のNaNエントリを持つ配列で動作する場合、その戻り値には、元の配列ではNaNであった各エントリのNaNが含まれます。

例:
a = random.randint(5、size = 100).astype(float)

a [12] = nan#単一のnanエントリを追加
ユニーク(a)
array([0.、1.、2.、3.、4.、NaN])
a [20] = nan#秒を追加
ユニーク(a)
array([0.、1.、2.、3.、4.、NaN、NaN])
a [13] = nan
ユニーク(a)#そして3番目
array([0.、1.、2.、3.、4.、NaN、NaN、NaN])

これはおそらく、xとyの両方がNaNの場合、x == yがFalseと評価されるという事実によるものです。 一意には、すでに識別された値に値が存在するかどうかをチェックする条件に「または(isnan(x)and isnan(y))」を追加する必要があります。 numpyでユニークな生活をしていて、探しに行っても見つからなかったので、自分で変更を加えることはできません(または、条件の正確な構文がどうあるべきかを確認することさえできません)。

また、次の関数を使用して、動作にパッチを適用できます。

def nanunique(x):
a = numpy.unique(x)
r = []
私のために:
iがrまたは(numpy.isnan(i)およびnumpy.any(numpy.isnan(r)))の場合:
継続する
そうしないと:
r.append(i)
numpy.array(r)を返します

00 - Bug Other

最も参考になるコメント

私は今日同じ問題に遭遇しました。 np.uniqueルーチンの中核は、numpy / lib / arraysetops.py内の解明されたソート済み配列のマスクを計算して、そのソート済み配列の値がいつ変更されるかを見つけることです。

mask = np.empty(aux.shape, dtype=np.bool_)
mask[:1] = True
mask[1:] = aux[1:] != aux[:-1]

これは、次のようなものに置き換えることができます。これは、約5年前のjaimefrioのコメントにほぼ沿っていますが、argmin呼び出しを回避します。

mask = np.empty(aux.shape, dtype=np.bool_)
mask[:1] = True
if (aux.shape[0] > 0 and isinstance(aux[-1], (float, np.float16,
                                              np.float32, np.float64))
    and np.isnan(aux[-1])):
    aux_firstnan = np.searchsorted(aux, np.nan, side='left')
    mask[1:aux_firstnan] = (aux[1:aux_firstnan] != aux[:aux_firstnan-1])
    mask[aux_firstnan] = True
    mask[aux_firstnan+1:] = False
else:
    mask[1:] = aux[1:] != aux[:-1]

いくつかの%timeit実験を実行すると、アレイが大きく、NaNが非常に少ない場合(たとえば、100万のうち10 NaN)、最大で10%未満のランタイムペナルティが観察されました。このような大きなアレイの場合、実際には、多くの場合、より高速に実行されます。 NaNの。

一方、配列が小さい場合(たとえば、10エントリ)、floatとNaNのチェックは比較的高価であり、実行時間は最大で倍になる可能性があるため、パフォーマンスが大幅に低下します。 これは、チェックが遅いため、NaNがない場合でも当てはまります。

アレイにNaNがある場合は、NaNを組み合わせて異なる結果が生成されます。これが、すべてのポイントです。 したがって、その場合、望ましい結果(すべてのNaNを単一の値グループに結合)を取得するのと、望ましくない結果(独自の値グループ内の各NaN)をわずかに速く取得するのは実際には問題です。

最後に、このパッチでは、次の例のように、NaNを含む複合オブジェクトに関連する一意の値の検索が修正されないことに注意してください。

a = np.array([[0,1],[np.nan, 1], [np.nan, 1]])
np.unique(a, axis=0)

それでも戻るだろう

array([[ 0.,  1.],
       [nan,  1.],
       [nan,  1.]])

全てのコメント14件

_tracユーザーrspringuelが2010-06-18に書き込みました_

上記のコードブロックを使用するために、撃ちます。 これはパッチオーバーコードにのみ実際に影響するので、それを再投稿します。

def nanunique(x):
    a = numpy.unique(x)
    r = []
    for i in a:
        if i in r or (numpy.isnan(i) and numpy.any(numpy.isnan(r))):
            continue
        else:
            r.append(i)
    return numpy.array(r)

修繕。

最新のマスターでこの問題がまだ発生しています。 どのコミットがそれを修正すべきでしたか? 何かが足りない場合を除いて、この問題を再度開くことをお勧めします。

これはfloatの場合は簡単に修正できますが、複雑なdtypeや構造化されたdtypeの場合は簡単な方法がわかりません。 簡単なPRをまとめ、そこでオプションについて話し合うことができます。

@jaimefrio私はそれをユニークな使用のために修正しました

    if issubclass(aux.dtype.type, np.inexact):
        # nans always compare unequal, so encode as integers
        tmp = aux.searchsorted(aux)
    else:
        tmp = aux
    flag = np.concatenate(([True], tmp[1:] != tmp[:-1]))

しかし、他のすべての操作にも問題があるようです。 たぶん、 nan_equal, nan_not_equal ufuncs、またはnanfuntionsの何かが必要です。

auxを自分でソート検索するのは、賢いトリックです。 そのすべてをソート検索するのは少し無駄ですが、理想的には、 auxflagを今のようにクレートした後、最初のエントリをnanで見つけたいと思います。 :

if not aux[-1] == aux[-1]:
    nanidx = np.argmin(aux == aux)
    nanaux = aux[nanidx:].searchsorted(aux[nanidx:])
    flag[nanidx+1:] = nanaux[1:] != nanaux[:-1]

または、私がそこに導入した可能性が高い1つのエラーによってすべてのオフを修正した後の同様の何か。

私のこの最後のアプローチは、float型と複合型では機能しますが、浮動小数点フィールドを持つ構造化dtypeでは失敗します。 しかし、検索ソートのトリックは、すべてのタイプで機能するとしても、無駄すぎると思います。 いくつかのタイミング:

In [10]: a = np.random.randn(1000)

In [11]: %timeit np.unique(a)
10000 loops, best of 3: 69.5 us per loop

In [12]: b = np.sort(a)

In [13]: %timeit b.searchsorted(b)
10000 loops, best of 3: 28.1 us per loop

これは40%のパフォーマンスヒットになります。これは、 nanunique関数では問題ないかもしれませんが、一般的なケースではおそらくそうではありません。

2019年と呼ばれ、OPの問題はまだ有効であり、コードは再現可能です。

@jaimefrioなぜ、デフォルトでfalseのオプションを設定できないのですか?

つまり、この動作はせいぜい混乱を招き、パフォーマンスは言い訳にはなりません。

@ Demetrio92この問題を解決しようと試みてくれたことに感謝しますが、インターネット上の皮肉/皮肉は人によって解釈が異なる可能性があります。親切にしてください。 私たちの中には、パフォーマンスが非常に重要であり、物事を遅くするコードを何気なく追加しない人もいます。

PR#5487は、前進する方法についてコメントしたり提案したりするのに適した場所かもしれません。

編集:PR番号を修正

この問題は8年間発生しているようですが、 numpy.uniqueのデフォルトの動作を高速ではなく正しくするために、+ 1を付けてチャイムを鳴らしたいと思います。 これは私のコードを壊しました、そして私は他の人がそれを持っている/苦しむであろうと確信しています。 オプションの「fast = False」を使用して、fastとnansのnanの動作を文書化できます。 np.uniqueが、タイムクリティカルなアプリケーションのパフォーマンスのボトルネックになることがよくあるとしたら、私は驚きます。

私は今日同じ問題に遭遇しました。 np.uniqueルーチンの中核は、numpy / lib / arraysetops.py内の解明されたソート済み配列のマスクを計算して、そのソート済み配列の値がいつ変更されるかを見つけることです。

mask = np.empty(aux.shape, dtype=np.bool_)
mask[:1] = True
mask[1:] = aux[1:] != aux[:-1]

これは、次のようなものに置き換えることができます。これは、約5年前のjaimefrioのコメントにほぼ沿っていますが、argmin呼び出しを回避します。

mask = np.empty(aux.shape, dtype=np.bool_)
mask[:1] = True
if (aux.shape[0] > 0 and isinstance(aux[-1], (float, np.float16,
                                              np.float32, np.float64))
    and np.isnan(aux[-1])):
    aux_firstnan = np.searchsorted(aux, np.nan, side='left')
    mask[1:aux_firstnan] = (aux[1:aux_firstnan] != aux[:aux_firstnan-1])
    mask[aux_firstnan] = True
    mask[aux_firstnan+1:] = False
else:
    mask[1:] = aux[1:] != aux[:-1]

いくつかの%timeit実験を実行すると、アレイが大きく、NaNが非常に少ない場合(たとえば、100万のうち10 NaN)、最大で10%未満のランタイムペナルティが観察されました。このような大きなアレイの場合、実際には、多くの場合、より高速に実行されます。 NaNの。

一方、配列が小さい場合(たとえば、10エントリ)、floatとNaNのチェックは比較的高価であり、実行時間は最大で倍になる可能性があるため、パフォーマンスが大幅に低下します。 これは、チェックが遅いため、NaNがない場合でも当てはまります。

アレイにNaNがある場合は、NaNを組み合わせて異なる結果が生成されます。これが、すべてのポイントです。 したがって、その場合、望ましい結果(すべてのNaNを単一の値グループに結合)を取得するのと、望ましくない結果(独自の値グループ内の各NaN)をわずかに速く取得するのは実際には問題です。

最後に、このパッチでは、次の例のように、NaNを含む複合オブジェクトに関連する一意の値の検索が修正されないことに注意してください。

a = np.array([[0,1],[np.nan, 1], [np.nan, 1]])
np.unique(a, axis=0)

それでも戻るだろう

array([[ 0.,  1.],
       [nan,  1.],
       [nan,  1.]])

「アレイにNaNがある場合は、NaNを組み合わせて、異なる結果が生成されます。これがすべてのポイントです。」

+1

繰り返される要素を含むリストを返す関数(たとえば、1つ以上のNaNを持つリスト)は、「一意」と呼ばれるべきではありません。 NaNの場合に要素を繰り返す必要がある場合は、 numpy.unique(..., keep_NaN=False)など、デフォルトで無効になっている特殊なケースのみにする必要があります。

@ufmayerはPRを提出します!

+1
NaNを1回だけ返すこともサポートします

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