web-dev-qa-db-ja.com

Numpy:値の最初のインデックスをすばやく見つける

Numpy配列内の数値の最初の出現のインデックスを見つけるにはどうすればよいですか?スピードは私にとって重要です。次の回答には興味がありません。アレイ全体をスキャンし、最初に見つかったときに停止しないからです。

itemindex = numpy.where(array==item)[0][0]
nonzero(array == item)[0][0]

注1:その質問からの回答はどれも関連性がないようです 配列内の何かの最初のインデックスを返すNumpy関数はありますか?

注2:PythonループよりもCコンパイル済みメソッドを使用することをお勧めします。

93
cyborg

Numpy 2.0.0で予定されているこの機能のリクエストがあります: https://github.com/numpy/numpy/issues/2269

51
cyborg

あなたにとっては遅すぎますが、将来の参考のために:numpy( 1 )を使用するのが、numpyが実装するまでの最も簡単な方法です。 anaconda pythonディストリビューションを使用する場合、既にインストールされているはずです。コードがコンパイルされるため、高速になります。

@jit(nopython=True)
def find_first(item, vec):
    """return the index of the first occurence of item in vec"""
    for i in xrange(len(vec)):
        if item == vec[i]:
            return i
    return -1

その後:

>>> a = array([1,7,8,32])
>>> find_first(8,a)
2
28
tal

いくつかの方法のベンチマークを作成しました。

  • argwhere
  • 質問のようにnonzero
  • .tostring() @Rob Reilinkの答えのように
  • pythonループ
  • Fortranループ

Python および Fortran コードが利用可能です。リストへの変換など、見込みのないものはスキップしました。

ログスケールの結果。 X軸は針の位置です(配列のさらに下にあるかどうかを調べるのに時間がかかります)。最後の値は、配列にない針です。 Y軸はそれを見つける時間です。

benchmark results

配列には100万個の要素があり、テストは100回実行されました。結果はまだ少し変動しますが、定性的な傾向は明確です。Pythonとf2pyは最初の要素で終了するため、スケーリングが異なります。針が最初の1%にない場合、Pythonは非常に遅くなりますが、f2pyは高速です(ただし、コンパイルする必要があります)。

まとめると、f2pyが最速の解決策です。特に針がかなり早く現れる場合。

迷惑な組み込みではありませんが、実際にはわずか2分の作業です。 search.f90というファイルに this を追加します。

subroutine find_first(needle, haystack, haystack_length, index)
    implicit none
    integer, intent(in) :: needle
    integer, intent(in) :: haystack_length
    integer, intent(in), dimension(haystack_length) :: haystack
!f2py intent(inplace) haystack
    integer, intent(out) :: index
    integer :: k
    index = -1
    do k = 1, haystack_length
        if (haystack(k)==needle) then
            index = k - 1
            exit
        endif
    enddo
end

integer以外のものを探している場合は、タイプを変更してください。次に、次を使用してコンパイルします。

f2py -c -m search search.f90

その後、次のことができます(Pythonから):

import search
print(search.find_first.__doc__)
a = search.find_first(your_int_needle, your_int_array)
17
Mark

array.tostring()を使用してからfind()メソッドを使用して、ブール配列をPython文字列に変換できます。

(array==item).tostring().find('\x01')

ただし、Python文字列は不変である必要があるため、これにはデータのコピーが含まれます。利点は、たとえば\x00\x01を見つけることによる立ち上がりエッジ

11
Rob Reilink

ソートされた配列の場合、np.searchsortedが機能します。

9
bubu

別の方法といくつかのアプリオリ配列の知識が本当に役立つという問題に遭遇したと思います。データの最初のYパーセントで答えを見つける確率がXであるようなもの。ラッキーになることを期待して問題を分割し、pythonでネストされたリスト内包表記などでこれを行う。

ctypes を使用しても、このブルートフォースを実行するC関数を作成するのはそれほど難しくありません。

私が一緒にハックしたCコード(index.c):

long index(long val, long *data, long length){
    long ans, i;
    for(i=0;i<length;i++){
        if (data[i] == val)
            return(i);
    }
    return(-999);
}

そして、python:

# to compile (mac)
# gcc -shared index.c -o index.dylib
import ctypes
lib = ctypes.CDLL('index.dylib')
lib.index.restype = ctypes.c_long
lib.index.argtypes = (ctypes.c_long, ctypes.POINTER(ctypes.c_long), ctypes.c_long)

import numpy as np
np.random.seed(8675309)
a = np.random.random_integers(0, 100, 10000)
print lib.index(57, a.ctypes.data_as(ctypes.POINTER(ctypes.c_long)), len(a))

92を取得します。

pythonを適切な関数にまとめると、そこに行きます。

このシードでは、Cバージョンの方がはるかに高速です(〜20倍)(timeitが苦手です)

import timeit
t = timeit.Timer('np.where(a==57)[0][0]', 'import numpy as np; np.random.seed(1); a = np.random.random_integers(0, 1000000, 10000000)')
t.timeit(100)/100
# 0.09761879920959472
t2 = timeit.Timer('lib.index(57, a.ctypes.data_as(ctypes.POINTER(ctypes.c_long)), len(a))', 'import numpy as np; np.random.seed(1); a = np.random.random_integers(0, 1000000, 10000000); import ctypes; lib = ctypes.CDLL("index.dylib"); lib.index.restype = ctypes.c_long; lib.index.argtypes = (ctypes.c_long, ctypes.POINTER(ctypes.c_long), ctypes.c_long) ')
t2.timeit(100)/100
# 0.005288000106811523
7
Brian Larsen

@talは、最初のインデックスを見つけるためにnumba関数を既に提示しましたが、これは1D配列でのみ機能します。 np.ndenumerate を使用すると、任意の次元の配列で最初のインデックスを見つけることもできます。

from numba import njit
import numpy as np

@njit
def index(array, item):
    for idx, val in np.ndenumerate(array):
        if val == item:
            return idx
    return None

サンプルケース:

>>> arr = np.arange(9).reshape(3,3)
>>> index(arr, 3)
(1, 0)

タイミングは、パフォーマンスが tals ソリューションに似ていることを示しています。

arr = np.arange(100000)
%timeit index(arr, 5)           # 1000000 loops, best of 3: 1.88 µs per loop
%timeit find_first(5, arr)      # 1000000 loops, best of 3: 1.7 µs per loop

%timeit index(arr, 99999)       # 10000 loops, best of 3: 118 µs per loop
%timeit find_first(99999, arr)  # 10000 loops, best of 3: 96 µs per loop
3
MSeifert

リストがsortedの場合、very quick 'bisect'パッケージでインデックスを検索できます。 O(n)ではなくO(log(n))です。

bisect.bisect(a, x)

配列aでxを見つけます。ソートされた場合、最初のすべての要素を通過するCルーチンよりも確実に高速です(リストが十分に長い場合)。

時々知るのは良いことです。

2
ngrislain

私は仕事のためにこれが必要だったので、私はPythonとNumpyのCインターフェイスを自分で学び、自分で書きました。 http://Pastebin.com/GtcXuLyd これは1次元配列のみに対応していますが、ほとんどのデータ型(int、float、またはstring)で機能し、テストにより、純粋なPython-numpyで予想されるアプローチよりも約20倍高速であることが示されました。

1
dpitch40

私が知る限り、ブール配列のnp.anyとnp.allのみが短絡されます。

あなたの場合、numpyはブール条件を作成するために1回、インデックスを見つけるために2回、配列全体を2回調べる必要があります。

この場合の推奨事項は、cythonを使用することです。この場合の例を調整するのは簡単だと思います。特に、さまざまなdtypeや形状に大きな柔軟性が必要ない場合はそうです。

1
Josef

長年のmatlabユーザーとして、私はこの問題の効率的な解決策をかなり長い間探していました。最後に、議論に動機付けられて、この提案が提案されました thread 提案されたものに似たAPIを実装するソリューションを考え出そうとしました here 1Dアレイ。

このように使用します

import numpy as np
import utils_find_1st as utf1st
array = np.arange(100000)
item = 1000
ind = utf1st.find_1st(array, item, utf1st.cmp_larger_eq)

サポートされる条件演算子は、cmp_equal、cmp_not_equal、cmp_larger、cmp_smaller、cmp_larger_eq、cmp_smaller_eqです。効率のために、拡張はcで記述されています。

ここで、ソース、ベンチマーク、その他の詳細を確認できます。

https://pypi.python.org/pypi?name=py_find_1st&:action=display

私たちのチームで使用するために(Linuxおよびmacosのアナコンダ)インストールを簡素化するanacondaインストーラーを作成しました。ここで説明するように使用できます。

https://anaconda.org/roebel/py_find_1st

1
A Roebel

これはどう

import numpy as np
np.amin(np.where(array==item))
0
nkvnkv

検索のシーケンスを実行している場合、検索ディメンションが十分に大きくない場合、文字列への変換などの巧妙な処理によるパフォーマンスの向上が外側のループで失われる可能性があることに注意してください。上記で提案された文字列変換のトリックを使用するfind1と、内側の軸に沿ってargmaxを使用するfind2の反復のパフォーマンスを確認します(さらに、-1として不一致が返されるように調整します)

import numpy,time
def find1(arr,value):
    return (arr==value).tostring().find('\x01')

def find2(arr,value): #find value over inner most axis, and return array of indices to the match
    b = arr==value
    return b.argmax(axis=-1) - ~(b.any())


for size in [(1,100000000),(10000,10000),(1000000,100),(10000000,10)]:
    print(size)
    values = numpy.random.choice([0,0,0,0,0,0,0,1],size=size)
    v = values>0

    t=time.time()
    numpy.apply_along_axis(find1,-1,v,1)
    print('find1',time.time()-t)

    t=time.time()
    find2(v,1)
    print('find2',time.time()-t)

出力

(1, 100000000)
('find1', 0.25300002098083496)
('find2', 0.2780001163482666)
(10000, 10000)
('find1', 0.46200013160705566)
('find2', 0.27300000190734863)
(1000000, 100)
('find1', 20.98099994659424)
('find2', 0.3040001392364502)
(10000000, 10)
('find1', 206.7590000629425)
('find2', 0.4830000400543213)

とは言っても、Cで記述された検索結果は、これらのアプローチのいずれよりも少なくとも少し速くなります。

0
dlm