Numpy: ค้นหาดัชนีแรกของมูลค่าอย่างรวดเร็ว


105

ฉันจะค้นหาดัชนีของการเกิดครั้งแรกของตัวเลขในอาร์เรย์ Numpy ได้อย่างไร ความเร็วเป็นสิ่งสำคัญสำหรับฉัน ฉันไม่สนใจคำตอบต่อไปนี้เพราะพวกเขาสแกนอาร์เรย์ทั้งหมดและไม่หยุดเมื่อพบเหตุการณ์แรก:

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

หมายเหตุ 1: ไม่มีคำตอบจากคำถามนั้นที่เกี่ยวข้องมีฟังก์ชัน Numpy เพื่อส่งคืนดัชนีแรกของบางสิ่งในอาร์เรย์หรือไม่?

หมายเหตุ 2: แนะนำให้ใช้วิธีการคอมไพล์ C กับลูป Python

คำตอบ:


57

มีการร้องขอคุณสมบัติสำหรับสิ่งนี้ที่กำหนดไว้สำหรับ Numpy 2.0.0: https://github.com/numpy/numpy/issues/2269


42
ก้าวไปข้างหน้าอย่างรวดเร็วในปี 2018 ปัญหาดูเหมือนจะไม่ขยับเลยสักนิด
P-Gn

7
และ Numpy ยังคงเป็น 1.xx
Ian Lin

30

แม้ว่ามันจะสายเกินไปสำหรับคุณ แต่สำหรับการอ้างอิงในอนาคต: การใช้ numba ( 1 ) เป็นวิธีที่ง่ายที่สุดจนกว่า numpy จะนำไปใช้ หากคุณใช้การแจกจ่ายงูใหญ่อนาคอนดาควรติดตั้งไว้แล้ว โค้ดจะถูกคอมไพล์จึงรวดเร็ว

@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

4
สำหรับ python3 xrangeจำเป็นต้องเปลี่ยนสำหรับrange.

การปรับปรุงโค้ดเล็กน้อยใน Python 3+: use enumerateas in for i, v in enumerate(vec):; if v == item: return i. (นี่ไม่ใช่ความคิดที่ดีใน Python <= 2.7 ที่enumerateสร้างรายการแทนที่จะเป็นตัววนซ้ำพื้นฐาน)
acdr

23

ฉันได้สร้างเกณฑ์มาตรฐานสำหรับหลายวิธี:

  • argwhere
  • nonzero เช่นเดียวกับในคำถาม
  • .tostring() เช่นเดียวกับคำตอบของ @Rob Reilink
  • หลามลูป
  • ห่วง Fortran

งูหลามและFortranรหัสที่มีอยู่ ฉันข้ามคนที่ไม่มีแนวโน้มเช่นการแปลงเป็นรายการ

ผลลัพธ์ในมาตราส่วนบันทึก แกน X คือตำแหน่งของเข็ม (ใช้เวลานานกว่าจะพบว่ามันอยู่ไกลออกไปจากอาร์เรย์หรือไม่) ค่าสุดท้ายคือเข็มที่ไม่ได้อยู่ในอาร์เรย์ แกน Y คือเวลาในการค้นหา

ผลการเปรียบเทียบ

อาร์เรย์มี 1 ล้านองค์ประกอบและการทดสอบดำเนินการ 100 ครั้ง ผลลัพธ์ยังคงผันผวนเล็กน้อย แต่แนวโน้มเชิงคุณภาพนั้นชัดเจน: Python และ f2py ออกจากองค์ประกอบแรกจึงมีขนาดแตกต่างกัน Python จะช้าเกินไปถ้าเข็มไม่อยู่ใน 1% แรกในขณะที่f2pyเร็ว (แต่คุณต้องรวบรวม)

สรุปแล้วf2py เป็นวิธีแก้ปัญหาที่เร็วที่สุดโดยเฉพาะอย่างยิ่งถ้าเข็มปรากฏขึ้นค่อนข้างเร็ว

มันไม่ได้สร้างขึ้นมาเพื่อให้น่ารำคาญ แต่มันใช้งานได้เพียง 2 นาทีเท่านั้น เพิ่มสิ่งนี้ลงในไฟล์ชื่อ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)

2
เหตุใดf2py1 รายการจึงช้ากว่า 10 รายการ
Eric

2
@Eric ฉันเดาว่าที่สเกลเหล่านั้น (10e-6) นั่นเป็นเพียงสัญญาณรบกวนในข้อมูลและความเร็วจริงต่อรายการนั้นเร็วมากมันไม่ได้มีส่วนช่วยในเวลาโดยรวมที่ n <100 หรือมากกว่านั้น
Brendan

11

คุณสามารถแปลงอาร์เรย์บูลีนเป็นสตริง Python โดยใช้array.tostring()แล้วใช้เมธอด find ():

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

สิ่งนี้เกี่ยวข้องกับการคัดลอกข้อมูลเนื่องจากสตริง Python จำเป็นต้องไม่เปลี่ยนรูป ข้อดีคือคุณสามารถค้นหาเช่นขอบที่เพิ่มขึ้นโดยการค้นหา\x00\x01


สิ่งนี้น่าสนใจ แต่แทบจะไม่เร็วกว่าเลยเพราะคุณยังต้องจัดการกับข้อมูลทั้งหมด (ดูคำตอบของฉันสำหรับเกณฑ์มาตรฐาน)
มาร์ค

10

ในกรณีที่อาร์เรย์เรียงลำดับใช้np.searchsortedงานได้


2
หากอาร์เรย์ไม่มีรายการนี้ตามความยาวอาร์เรย์ทั้งหมดจะถูกส่งกลับ
Boris Tsema

7

ฉันคิดว่าคุณประสบปัญหาที่วิธีการอื่นและความรู้เบื้องต้นเกี่ยวกับอาร์เรย์จะช่วยได้จริงๆ ชนิดของสิ่งที่คุณมีความน่าจะเป็น X ในการค้นหาคำตอบของคุณในเปอร์เซ็นต์ Y แรกของข้อมูล การแยกปัญหาด้วยความหวังว่าจะโชคดีจากนั้นทำสิ่งนี้ใน python ด้วยความเข้าใจในรายการที่ซ้อนกันหรือบางสิ่งบางอย่าง

การเขียนฟังก์ชัน C เพื่อทำแรงเดรัจฉานนี้ไม่ยากเกินไปโดยใช้ctypesเช่นกัน

รหัส 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

ห่อ python ให้เป็นฟังก์ชันที่เหมาะสมและไปที่นั่น

เวอร์ชัน C เร็วกว่ามาก (~ 20x) สำหรับเมล็ดพันธุ์นี้ (เตือนฉันไม่ดีกับ 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 doubles โดยค่าเริ่มต้น) คุณต้องคิดให้หนักขึ้นเล็กน้อยเนื่องจาก == ไม่ปลอดภัยจริง ๆ หรือสิ่งที่คุณต้องการสำหรับค่าทศนิยม อย่าลืมว่ามันเป็นความคิดที่ดีจริงๆเมื่อใช้ ctypes เพื่อพิมพ์อาร์เรย์จำนวนนับของคุณ
Brian Larsen

ขอบคุณ @Brian Larsen ฉันอาจจะลองดู ฉันคิดว่ามันเป็นคำขอคุณสมบัติที่ไม่สำคัญสำหรับการแก้ไขครั้งต่อไป
cyborg

6

@tal ได้นำเสนอnumbaฟังก์ชันเพื่อค้นหาดัชนีแรกแล้ว แต่ใช้ได้กับอาร์เรย์ 1D เท่านั้น ด้วย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

1
หากคุณสนใจมากขึ้นในการค้นหาตามแกนที่กำหนดก่อน: เปลี่ยนตำแหน่งarrayก่อนป้อนเข้าไปnp.ndenumerateเพื่อให้แกนความสนใจของคุณมาก่อน
CheshireCat

ขอบคุณนี่เป็นคำสั่งของขนาดที่เร็วกว่า: จาก ~ 171ms ( np.argwhere) ถึง 717ns (โซลูชันของคุณ) ทั้งสำหรับอาร์เรย์ของรูปร่าง(3000000, 12))
Arthur Colombini Gusmão

3

หากรายการของคุณได้รับการจัดเรียงคุณสามารถค้นหาดัชนีได้อย่างรวดเร็วด้วยแพ็คเกจ "bisect" เป็น O (log (n)) แทน O (n)

bisect.bisect(a, x)

ค้นหา x ในอาร์เรย์ a ซึ่งเร็วกว่าในกรณีที่เรียงลำดับมากกว่ารูทีน C ใด ๆ ที่ผ่านองค์ประกอบแรกทั้งหมด (สำหรับรายการที่ยาวพอ)

บางครั้งก็รู้ดี


>>> 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จะดีกว่าสำหรับอาร์เรย์ที่เรียงลำดับ (อย่างน้อยสำหรับ ints)
Boris Tsema

2

เท่าที่ฉันรู้มีเพียง np.any และ np.all บนอาร์เรย์บูลีนเท่านั้นที่ลัดวงจร

ในกรณีของคุณ numpy ต้องผ่านอาร์เรย์ทั้งหมดสองครั้งครั้งเดียวเพื่อสร้างเงื่อนไขบูลีนและครั้งที่สองเพื่อค้นหาดัชนี

คำแนะนำของฉันในกรณีนี้คือการใช้ cython ฉันคิดว่ามันควรจะง่ายในการปรับตัวอย่างสำหรับกรณีนี้โดยเฉพาะอย่างยิ่งถ้าคุณไม่ต้องการความยืดหยุ่นมากนักสำหรับประเภทและรูปร่างที่แตกต่างกัน


2

ฉันต้องการสิ่งนี้สำหรับงานของฉันดังนั้นฉันจึงสอนตัวเองด้วยอินเตอร์เฟส C ของ Python และ Numpy และเขียนของฉันเอง http://pastebin.com/GtcXuLydใช้สำหรับอาร์เรย์ 1-D เท่านั้น แต่ใช้ได้กับประเภทข้อมูลส่วนใหญ่ (int, float หรือ strings) และการทดสอบแสดงให้เห็นว่าเร็วกว่าวิธีที่คาดไว้อีกครั้งประมาณ 20 เท่าใน Python ที่บริสุทธิ์ - มึน


2

ปัญหานี้สามารถแก้ไขได้อย่างมีประสิทธิภาพด้วยตัวเลขบริสุทธิ์โดยการประมวลผลอาร์เรย์เป็นชิ้น ๆ :

def find_first(x):
    idx, step = 0, 32
    while idx < x.size:
        nz, = x[idx: idx + step].nonzero()
        if len(nz): # found non-zero, return it
            return nz[0] + idx
        # move to the next chunk, increase step
        idx += step
        step = min(9600, step + step // 2)
    return -1

stepอาร์เรย์จะถูกประมวลผลในก้อนขนาด ยิ่งstepขั้นตอนยาวมากเท่าไหร่การประมวลผล Zeroed Array ก็จะเร็วขึ้นเท่านั้น (กรณีที่แย่ที่สุด) ยิ่งมีขนาดเล็กการประมวลผลอาร์เรย์ก็จะเร็วขึ้นโดยไม่มีศูนย์ในช่วงเริ่มต้น เคล็ดลับคือการเริ่มต้นด้วยขนาดเล็กstepและเพิ่มขึ้นแบบทวีคูณ ยิ่งไปกว่านั้นไม่จำเป็นต้องเพิ่มให้สูงกว่าเกณฑ์เนื่องจากผลประโยชน์ที่ จำกัด

ฉันได้เปรียบเทียบโซลูชันกับโซลูชัน ndarary.nonzero และ numba บริสุทธิ์กับ 10 ล้านอาร์เรย์ลอย

import numpy as np
from numba import jit
from timeit import timeit

def find_first(x):
    idx, step = 0, 32
    while idx < x.size:
        nz, = x[idx: idx + step].nonzero()
        if len(nz):
            return nz[0] + idx
        idx += step
        step = min(9600, step + step // 2)
    return -1

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


SIZE = 10_000_000
# First only
x = np.empty(SIZE)

find_first_numba(x[:10])

print('---- FIRST ----')
x[:] = 0
x[0] = 1
print('ndarray.nonzero', timeit(lambda: x.nonzero()[0][0], number=100)*10, 'ms')
print('find_first', timeit(lambda: find_first(x), number=1000), 'ms')
print('find_first_numba', timeit(lambda: find_first_numba(x), number=1000), 'ms')

print('---- LAST ----')
x[:] = 0
x[-1] = 1
print('ndarray.nonzero', timeit(lambda: x.nonzero()[0][0], number=100)*10, 'ms')
print('find_first', timeit(lambda: find_first(x), number=100)*10, 'ms')
print('find_first_numba', timeit(lambda: find_first_numba(x), number=100)*10, 'ms')

print('---- NONE ----')
x[:] = 0
print('ndarray.nonzero', timeit(lambda: x.nonzero()[0], number=100)*10, 'ms')
print('find_first', timeit(lambda: find_first(x), number=100)*10, 'ms')
print('find_first_numba', timeit(lambda: find_first_numba(x), number=100)*10, 'ms')

print('---- ALL ----')
x[:] = 1
print('ndarray.nonzero', timeit(lambda: x.nonzero()[0][0], number=100)*10, 'ms')
print('find_first', timeit(lambda: find_first(x), number=100)*10, 'ms')
print('find_first_numba', timeit(lambda: find_first_numba(x), number=100)*10, 'ms')

และผลลัพธ์บนเครื่องของฉัน:

---- FIRST ----
ndarray.nonzero 54.733994480002366 ms
find_first 0.0013148509997336078 ms
find_first_numba 0.0002839310000126716 ms
---- LAST ----
ndarray.nonzero 54.56336712999928 ms
find_first 25.38929685000312 ms
find_first_numba 8.022820680002951 ms
---- NONE ----
ndarray.nonzero 24.13432420999925 ms
find_first 25.345200140000088 ms
find_first_numba 8.154927100003988 ms
---- ALL ----
ndarray.nonzero 55.753537260002304 ms
find_first 0.0014760300018679118 ms
find_first_numba 0.0004358099977253005 ms

บริสุทธิ์ndarray.nonzeroเป็นที่แน่นอนคลาย โซลูชัน numba เร็วกว่าประมาณ 5 เท่าสำหรับกรณีที่ดีที่สุด มันเร็วขึ้นประมาณ 3 เท่าในกรณีที่เลวร้ายที่สุด


2

หากคุณกำลังมองหาองค์ประกอบที่ไม่ใช่ศูนย์แรกคุณสามารถใช้แฮ็กต่อไปนี้:

idx = x.view(bool).argmax() // x.itemsize
idx = idx if x[idx] else -1

เป็นวิธีการแก้ปัญหาแบบ "ไม่บริสุทธิ์" ที่รวดเร็วมากแต่ก็ล้มเหลวในบางกรณีที่กล่าวถึงด้านล่าง

โซลูชันนี้ใช้ประโยชน์จากข้อเท็จจริงที่ว่าการแทนค่าศูนย์สำหรับประเภทตัวเลขประกอบด้วย0ไบต์ ใช้กับ numpy ได้boolเช่นกัน ในเวอร์ชันล่าสุดของ numpy argmax()ฟังก์ชันจะใช้ตรรกะการลัดวงจรเมื่อประมวลผลboolประเภท ขนาดเท่ากับbool1 ไบต์

ดังนั้นเราต้อง:

  • boolสร้างมุมมองของอาร์เรย์เป็น ไม่มีการสร้างสำเนา
  • ใช้argmax()เพื่อค้นหาไบต์แรกที่ไม่ใช่ศูนย์โดยใช้ตรรกะการลัดวงจร
  • คำนวณค่าชดเชยของไบต์นี้ใหม่กับดัชนีขององค์ประกอบที่ไม่ใช่ศูนย์แรกโดยการหารจำนวนเต็ม (ตัวดำเนินการ//) ของค่าชดเชยตามขนาดขององค์ประกอบเดียวที่แสดงเป็นไบต์ ( x.itemsize)
  • ตรวจสอบว่าx[idx]ไม่ใช่ศูนย์จริงหรือไม่เพื่อระบุกรณีเมื่อไม่มีค่าที่ไม่ใช่ศูนย์

ฉันได้สร้างมาตรฐานเทียบกับโซลูชัน numba และสร้างมันnp.nonzeroขึ้นมา

import numpy as np
from numba import jit
from timeit import timeit

def find_first(x):
    idx = x.view(bool).argmax() // x.itemsize
    return idx if x[idx] else -1

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


SIZE = 10_000_000
# First only
x = np.empty(SIZE)

find_first_numba(x[:10])

print('---- FIRST ----')
x[:] = 0
x[0] = 1
print('ndarray.nonzero', timeit(lambda: x.nonzero()[0][0], number=100)*10, 'ms')
print('find_first', timeit(lambda: find_first(x), number=1000), 'ms')
print('find_first_numba', timeit(lambda: find_first_numba(x), number=1000), 'ms')

print('---- LAST ----')
x[:] = 0
x[-1] = 1
print('ndarray.nonzero', timeit(lambda: x.nonzero()[0][0], number=100)*10, 'ms')
print('find_first', timeit(lambda: find_first(x), number=100)*10, 'ms')
print('find_first_numba', timeit(lambda: find_first_numba(x), number=100)*10, 'ms')

print('---- NONE ----')
x[:] = 0
print('ndarray.nonzero', timeit(lambda: x.nonzero()[0], number=100)*10, 'ms')
print('find_first', timeit(lambda: find_first(x), number=100)*10, 'ms')
print('find_first_numba', timeit(lambda: find_first_numba(x), number=100)*10, 'ms')

print('---- ALL ----')
x[:] = 1
print('ndarray.nonzero', timeit(lambda: x.nonzero()[0][0], number=100)*10, 'ms')
print('find_first', timeit(lambda: find_first(x), number=100)*10, 'ms')
print('find_first_numba', timeit(lambda: find_first_numba(x), number=100)*10, 'ms')

ผลลัพธ์ในเครื่องของฉันคือ:

---- FIRST ----
ndarray.nonzero 57.63976670001284 ms
find_first 0.0010841979965334758 ms
find_first_numba 0.0002308919938514009 ms
---- LAST ----
ndarray.nonzero 58.96685277999495 ms
find_first 5.923203580023255 ms
find_first_numba 8.762269750004634 ms
---- NONE ----
ndarray.nonzero 25.13398071998381 ms
find_first 5.924289370013867 ms
find_first_numba 8.810063839919167 ms
---- ALL ----
ndarray.nonzero 55.181210660084616 ms
find_first 0.001246920000994578 ms
find_first_numba 0.00028766007744707167 ms

วิธีแก้ปัญหานี้เร็วกว่า numba 33% และเป็นแบบ "numpy-pure"

ข้อเสีย:

  • ใช้ไม่ได้กับประเภทที่ยอมรับได้เช่น object
  • ล้มเหลวสำหรับศูนย์ลบที่บางครั้งปรากฏในfloatหรือdoubleการคำนวณ

นี่เป็นวิธีแก้ปัญหาที่บริสุทธิ์ที่สุดที่ ive พยายาม ควรได้รับคำตอบ @tstanisl ive พยายามหาวิธีแก้ปัญหาที่รวดเร็วในทำนองเดียวกันเพื่อค้นหาองค์ประกอบศูนย์แรกในอาร์เรย์ แต่มันมักจะจบลงช้ากว่าการแปลงเป็นบูลจากนั้นเรียกใช้ argmin () ความคิดใด ๆ
Ta946

1
@ Ta946. ไม่สามารถใช้เคล็ดลับเมื่อค้นหารายการที่เป็นศูนย์ เช่นคู่ที่ไม่ใช่ศูนย์อาจมีศูนย์ไบต์อยู่ หากคุณมองหาวิธีแก้ปัญหาที่ไม่บริสุทธิ์ให้ลองแก้ไขคำตอบอื่นของฉัน ดูstackoverflow.com/a/58294774/4989451 เพียงแค่ปฏิเสธชิ้นของก่อนที่จะเรียกx nonzero()มีแนวโน้มที่จะช้ากว่า numba แต่ ** จะไม่ ** ค้นหาในอาร์เรย์ทั้งหมดในขณะที่ค้นหารายการที่เป็นศูนย์แรกดังนั้นจึงอาจเร็วพอสำหรับความต้องการของคุณ
tstanisl

1

ในฐานะผู้ใช้ matlab มานานฉันได้ค้นหาวิธีแก้ปัญหาที่มีประสิทธิภาพสำหรับปัญหานี้มาระยะหนึ่งแล้ว สุดท้ายได้รับแรงบันดาลใจจากการอภิปรายข้อเสนอในชุดข้อความนี้ฉันได้พยายามหาวิธีแก้ปัญหาที่ใช้ API คล้ายกับที่แนะนำไว้ที่นี่โดยรองรับเฉพาะอาร์เรย์ 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

สำหรับการใช้งานในทีมของเรา (anaconda บน linux และ macos) ฉันได้สร้างตัวติดตั้ง anaconda ที่ช่วยให้การติดตั้งง่ายขึ้นคุณสามารถใช้มันตามที่อธิบายไว้ที่นี่

https://anaconda.org/roebel/py_find_1st


"ในฐานะผู้ใช้ matlab มานาน" - matlab สะกดอย่างไร
Eric

find (X, n) ค้นหาดัชนี n ตัวแรกโดยที่ X ไม่ใช่ศูนย์ mathworks.com/help/matlab/ref/find.html
A Roebel

0

โปรดทราบว่าหากคุณทำการค้นหาตามลำดับประสิทธิภาพที่ได้รับจากการทำสิ่งที่ชาญฉลาดเช่นการแปลงเป็นสตริงอาจหายไปในวงนอกหากมิติการค้นหาไม่ใหญ่พอ ดูว่าประสิทธิภาพของการวนซ้ำ find1 ที่ใช้เคล็ดลับการแปลงสตริงที่เสนอข้างต้นและ find2 ที่ใช้ argmax ตามแกนด้านในเป็นอย่างไร (บวกการปรับปรุงเพื่อให้แน่ใจว่าผลตอบแทนที่ไม่ตรงกันเป็น -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

เกี่ยวกับเรื่องนี้

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

2
แม้ว่ารหัสนี้อาจตอบคำถาม แต่การให้บริบทเพิ่มเติมเกี่ยวกับสาเหตุและ / หรือวิธีการตอบคำถามจะช่วยเพิ่มมูลค่าในระยะยาวได้อย่างมาก โปรดแก้ไขคำตอบของคุณเพื่อเพิ่มคำอธิบาย
Toby Speight

1
ฉันค่อนข้างมั่นใจว่านี่ช้ากว่าwhere(array==item)[0][0]คำถาม ...
มาร์ค

-1

คุณสามารถปกปิดอาร์เรย์ของคุณเป็น a listและใช้index()วิธีการ:

i = list(array).index(item)

เท่าที่ฉันทราบนี่เป็นวิธีการคอมไพล์ C


3
สิ่งนี้น่าจะช้ากว่าการรับผลลัพธ์แรกจาก np หลายเท่าที่ไหน
cwa

1
จริงมาก .. ฉันใช้timeit()อาร์เรย์จำนวน 10,000 จำนวนเต็ม - การแปลงเป็นรายการช้าลงประมาณ 100 เท่า! ฉันลืมไปแล้วว่าโครงสร้างข้อมูลพื้นฐานสำหรับอาร์เรย์ numpy นั้นแตกต่างจากรายการมาก ..
drevicko
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.