2011-10-03 26 views
78

如何查找Numpy數組中第一次出現的索引號? 速度對我很重要。我不喜歡下面的答案,因爲他們掃描整個數組,當他們找到第一次出現不停止:Numpy:快速找到第一個索引值

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

注1:沒有從這個問題的答案似乎相關Is there a Numpy function to return the first index of something in an array?

注2:使用C編譯方法優於Python循環。

回答

0

您可以使用.data屬性在numpy陣列上獲得讀寫緩衝區。迭代,但你需要知道你的數據是主要行還是列(使用ndarray.shapenumpy.unravel_index將平面索引轉換回索引元組)。

+0

是你建議我使用python迭代而不是ufunc?這比numpy更有效嗎? – cyborg

+0

我不確定,我甚至不知道ufuncs。聽起來你已經比我更瞭解了! – wim

7

我覺得你遇到了一個問題,其中一個不同的方法和一些先驗知識的數組真的會有所幫助。在Y數據的第一個百分比中,您有X的概率找到您的答案。分裂的問題,希望得到幸運,然後在嵌套列表理解或Python的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); 
} 

和蟒蛇:

# 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

結束語蟒蛇成適當功能,你去了。

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 
+1

如果數組是雙精度的(記住python float是默認的C double),那麼你必須考慮一點,因爲==不是很安全,或者你想要浮點值。另外不要忘記,使用ctypes鍵入numpy數組時,這是一個非常好的主意。 –

+0

謝謝@Brian Larsen。我可以試試看。我認爲這是對下一次numpy修訂的簡單功能要求。 – cyborg

+0

我同意功能請求的想法,這顯然是最好的方法。 –

1

據我只知道np.any和np.all布爾數組短路。

在你的情況,numpy必須遍歷整個數組兩次,一次創建布爾條件,第二次找到索引。

我在這種情況下的建議是使用cython。我認爲應該很容易爲這種情況調整一個例子,特別是如果你不需要對不同的dtypes和形狀有很大的靈活性。

1

我需要爲我的工作,所以我自學Python和NumPy的的C接口和寫我自己。 http://pastebin.com/GtcXuLyd它僅適用於一維數組,但適用於大多數數據類型(int,float或strings),並且測試表明它比純Python-numpy中的預期方法再快大約20倍。

-1

您可以隱蔽你的陣列爲list,並使用它的方法index()

i = list(array).index(item) 

據我所知,這是一個C編譯的方法。

+3

這很可能比剛剛從np.where – cwa

+0

得到的第一個結果慢很多倍。我對10000個整數數組使用'timeit()' - 轉換爲列表的速度大約慢100倍!我忘記了numpy數組的底層數據結構與列表非常不同 – drevicko

3

如果列表是排序,可以實現與「平分」包很快搜索索引。 它是O(log(n))而不是O(n)。

bisect.bisect(a, x) 

計算x的陣列的所示,在分選的情況下比任何C-例程通過所有的第一元件去(足夠長的列表)絕對更快。

有時候很好。

+0

>>> cond =「import numpy as np; a = np.arange(40)」''' ' timeit(「np.searchsorted(a,39)」,cond)'適用於3.47867107391秒。 'timeit(「bisect.bisect(a,39)」,cond2)'適用於7.0661458969116秒。 看起來'numpy.searchsorted'對於排序後的數組來說更好(至少對於整數)。 –

10

可以使用array.tostring(),然後用find()方法轉換爲布爾矩陣爲Python字符串:

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

這並不涉及複製數據,不過,因爲Python字符串必須是不可改變的。一個優點是您還可以搜索例如通過尋找\x00\x01

+0

這很有趣,但幾乎沒有更快,因爲您仍然需要處理所有數據(請參閱我的答案以獲得基準)。 – Mark

7

如果排序的數組np.searchsorted起作用。

+1

如果數組中沒有這個項目,那麼所有數組長度都將被返回。 –

0

只需要注意,如果您正在執行一系列搜索,如果搜索維度不夠大,從外部循環中執行諸如轉換爲字符串這樣的巧妙操作所帶來的性能提升可能會丟失。看到迭代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寫的會比這兩種方法

18

的至少快一點雖然是太晚了你,但以供將來參考: 使用numba(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 
+2

對於python3'xrange'需要更改'range'。 – light2yellow

0

這個怎麼樣

import numpy as np 
np.amin(np.where(array==item)) 
+1

儘管此代碼可能會回答問題,但提供 有關_why_和/或_how_的附加上下文,它將回答 該問題將顯着提高其長期值 的值。請[編輯]你的答案,添加一些解釋。 –

+1

我敢肯定,這個問題甚至比問題中的where(array == item)[0] [0]'慢...... – Mark

8

我做了一個標杆幾種方法:

  • argwhere
  • nonzero在這個問題
  • .tostring()在@Rob Reilink的回答
  • 蟒蛇循環
  • 的Fortran環

PythonFortran代碼是可用的。我跳過了無意識的轉換成列表。

對數級結果。 X軸是針的位置(需要更長的時間才能發現它是否在陣列的更遠處);最後一個值是不在陣列中的針。 Y軸是找到它的時間。

benchmark results

陣列具有百萬元件和測試跑100倍。結果仍然有些波動,但定性趨勢很明顯:Python和f2py在第一個元素處退出,因此它們的縮放比例不同。如果針頭不在第一個1%,Python變得太慢,而f2py很快(但你需要編譯它)。

總之,f2py是最快的解決方案,尤其是如果針頭顯得相當早。

它不是內置的,這是令人討厭的,但它只是2分鐘的工作。添加this一個叫search.f90文件:

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) 
+1

爲什麼f2py在1項比10慢? – Eric

2

@tal已經提出了numba功能找到的第一個索引但只適用於一維數組。隨着np.ndenumerate,你還可以找到的第一個索引在arbitarly維數組:

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