มีฟังก์ชั่น NumPy เพื่อส่งกลับดัชนีแรกของบางสิ่งในอาร์เรย์หรือไม่?


463

ฉันรู้ว่ามีวิธีสำหรับรายการ Python เพื่อกลับดัชนีแรกของบางสิ่ง:

>>> l = [1, 2, 3]
>>> l.index(2)
1

มีอะไรแบบนั้นสำหรับอาร์เรย์ NumPy หรือไม่


คำตอบ:


522

ใช่นี่คือคำตอบที่กำหนดให้กับอาร์เรย์ NumPy arrayและค่าitemเพื่อค้นหา:

itemindex = numpy.where(array==item)

ผลลัพธ์คือ tuple ที่มีดัชนีแถวแรกทั้งหมดจากนั้นดัชนีทั้งหมด

ตัวอย่างเช่นถ้าอาร์เรย์มีสองมิติและมันมีรายการของคุณในสองตำแหน่ง

array[itemindex[0][0]][itemindex[1][0]]

จะเท่ากับสินค้าของคุณและจะเป็นเช่นนั้น

array[itemindex[0][1]][itemindex[1][1]]

numpy.where


1
หากคุณกำลังมองหาแถวแรกที่มีรายการอยู่ในคอลัมน์แรกผลงานนี้ (แม้ว่าจะมีการโยนข้อผิดพลาดดัชนีหากไม่มีอยู่)rows, columns = np.where(array==item); first_idx = sorted([r for r, c in zip(rows, columns) if c == 0])[0]
BrT

27
ถ้าคุณต้องการหยุดค้นหาหลังจากค้นหาค่าแรก ฉันไม่คิดว่าที่ไหน () เปรียบได้กับการค้นหา ()
Michael Clerx

2
อา! หากคุณสนใจประสิทธิภาพลองดูคำตอบสำหรับคำถามนี้: stackoverflow.com/questions/7632963/…
Michael Clerx

11
np.argwhereจะมีประโยชน์มากขึ้นเล็กน้อยที่นี่:itemindex = np.argwhere(array==item)[0]; array[tuple(itemindex)]
Eric

3
เป็นที่น่าสังเกตว่าคำตอบนี้ถือว่าอาร์เรย์เป็นแบบ 2 มิติ whereทำงานบนอาเรย์ใด ๆ และจะคืนค่าความยาว 3 เมื่อใช้กับอาเรย์ 3 มิติ ฯลฯ
P. Camilleri

69

หากคุณต้องการดัชนีของการเกิดครั้งแรกที่มีเพียงค่าเดียวคุณสามารถใช้nonzero(หรือwhereจำนวนที่เท่ากันในกรณีนี้):

>>> t = array([1, 1, 1, 2, 2, 3, 8, 3, 8, 8])
>>> nonzero(t == 8)
(array([6, 8, 9]),)
>>> nonzero(t == 8)[0][0]
6

หากคุณต้องการดัชนีแรกของแต่ละ ค่าจำนวนมากคุณสามารถทำเช่นเดียวกันกับข้างบนซ้ำ ๆ ได้อย่างชัดเจน แต่มีเคล็ดลับที่อาจเร็วกว่า ต่อไปนี้จะพบว่าดัชนีขององค์ประกอบแรกของแต่ละsubsequence :

>>> nonzero(r_[1, diff(t)[:-1]])
(array([0, 3, 5, 6, 7, 8]),)

โปรดสังเกตว่ามันพบจุดเริ่มต้นของทั้งการเรียงตามลำดับจาก 3 วินาทีและเรียงตามลำดับจาก 8s ทั้งสอง:

[ 1 , 1, 1, 2 , 2, 3 , 8 , 3 , 8 , 8]

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

>>> st = sorted(t)
>>> nonzero(r_[1, diff(st)[:-1]])
(array([0, 3, 5, 7]),)

4
คุณช่วยอธิบายอะไรr_ได้บ้าง?
Geoff

1
@Geoff, r_concatenates; หรืออย่างแม่นยำมากขึ้นมันแปลวัตถุชิ้นเพื่อตัดแบ่งตามแต่ละแกน ฉันสามารถใช้hstackแทนได้ ที่อาจทำให้สับสนน้อยลง ดูเอกสารr_สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ c_นอกจากนี้ยังมี
Vebjorn Ljosa

+1 ดีจัง! (เทียบกับ NP.where) วิธีการแก้ปัญหาของคุณคือง่ายมาก (และอาจจะเร็วกว่า) ในกรณีที่มันเป็นเพียงเกิดขึ้นครั้งแรกของค่าที่กำหนดในอาร์เรย์ 1D ที่เราต้องการ
ดั๊ก

3
กรณีหลัง (หาดัชนีแรกของค่าทั้งหมด) ได้รับโดยvals, locs = np.unique(t, return_index=True)
askewchan

50

นอกจากนี้คุณยังสามารถแปลงอาร์เรย์ NumPy เป็นรายการในอากาศและรับดัชนี ตัวอย่างเช่น,

l = [1,2,3,4,5] # Python list
a = numpy.array(l) # NumPy array
i = a.tolist().index(2) # i will return index of 2
print i

มันจะพิมพ์ 1


อาจเป็นเพราะห้องสมุดมีการเปลี่ยนแปลงนับตั้งแต่มีการเขียนครั้งแรก แต่นี่เป็นทางออกแรกที่เหมาะกับฉัน
amracel

1
ฉันใช้ประโยชน์จากสิ่งนี้เพื่อค้นหาค่าหลายค่าในรายการโดยใช้รายการความเข้าใจ:[find_list.index(index_list[i]) for i in range(len(index_list))]
Matt Wenham

1
@MattWenham ถ้ามันใหญ่พอคุณสามารถแปลงของคุณfind_listไปยังอาร์เรย์ NumPy ของobject(หรืออะไรที่เฉพาะเจาะจงมากขึ้นที่เหมาะสม) find_arr[index_list]และเพียงแค่ทำ
Narfanar

ปิดหัวข้อทั้งหมด แต่นี่เป็นครั้งแรกที่ฉันเห็นวลี "ในอากาศ" - สิ่งที่ฉันได้เห็นมากที่สุดในสถานที่นั้นอาจเป็น "ในทันที"
flow2k

18

เพียงเพิ่มนักแสดงที่มีประโยชน์และมีประโยชน์ ทางเลือกจาก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
    # If no item was found return None, other return types might be a problem due to
    # numbas type inference.

นี่เป็นวิธีที่ค่อนข้างเร็วและเกี่ยวข้องกับอาร์เรย์หลายมิติตามธรรมชาติ :

>>> arr1 = np.ones((100, 100, 100))
>>> arr1[2, 2, 2] = 2

>>> index(arr1, 2)
(2, 2, 2)

>>> arr2 = np.ones(20)
>>> arr2[5] = 2

>>> index(arr2, 2)
(5,)

สิ่งนี้สามารถ ได้เร็วขึ้นมาก (เพราะมันลัดวงจรการดำเนินการ) มากกว่าวิธีการใด ๆ ที่ใช้หรือnp.wherenp.nonzero


อย่างไรก็ตามnp.argwhereยังสามารถจัดการกับอาร์เรย์หลายมิติได้อย่างสง่างาม (คุณจะต้องส่งมันด้วยตนเองไปยัง tuple และมันไม่ได้ลัดวงจร) แต่มันจะล้มเหลวหากไม่พบคู่ที่ตรงกัน:

>>> tuple(np.argwhere(arr1 == 2)[0])
(2, 2, 2)
>>> tuple(np.argwhere(arr2 == 2)[0])
(5,)

2
@njitเป็นชวเลขของjit(nopython=True)ฟังก์ชั่นจะถูกรวบรวมอย่างสมบูรณ์แบบ on-the-fly ในช่วงเวลาของการเรียกใช้ครั้งแรกเพื่อให้การโทรล่าม Python ถูกลบออกอย่างสมบูรณ์
bartolo-otrit

14

หากคุณกำลังจะใช้สิ่งนี้เป็นดัชนีในสิ่งอื่นคุณสามารถใช้ดัชนีบูลีนได้หากอาร์เรย์นั้นสามารถออกอากาศได้ คุณไม่ต้องการดัชนีที่ชัดเจน วิธีที่ง่ายที่สุดในการทำเช่นนี้คือการจัดทำดัชนีตามค่าความจริง

other_array[first_array == item]

การดำเนินการบูลีนใด ๆ ทำงาน:

a = numpy.arange(100)
other_array[first_array > 50]

วิธีการที่ไม่ใช่ศูนย์รับ booleans เช่นกัน:

index = numpy.nonzero(first_array == item)[0][0]

ศูนย์สองแห่งใช้สำหรับ tuple of indices (สมมติว่า first_array คือ 1D) จากนั้นรายการแรกในอาร์เรย์ของ indices


10

l.index(x)ส่งคืนi ที่เล็กที่สุดเช่นiคือดัชนีของการเกิดขึ้นครั้งแรกของ x ในรายการ

หนึ่งสามารถสันนิษฐานได้อย่างปลอดภัยว่ามีการใช้index()งานฟังก์ชั่นใน Python เพื่อให้หยุดทำงานหลังจากค้นหาคู่แรกและผลลัพธ์นี้มีประสิทธิภาพโดยเฉลี่ยที่ดีที่สุด

สำหรับการค้นหาองค์ประกอบที่หยุดหลังจากการจับคู่ครั้งแรกในอาร์เรย์ NumPy ให้ใช้ตัววนซ้ำ ( ndenumerate )

In [67]: l=range(100)

In [68]: l.index(2)
Out[68]: 2

อาร์เรย์ NumPy:

In [69]: a = np.arange(100)

In [70]: next((idx for idx, val in np.ndenumerate(a) if val==2))
Out[70]: (2L,)

โปรดทราบว่าทั้งวิธีการindex()และnextส่งคืนข้อผิดพลาดหากไม่พบองค์ประกอบ ด้วยnextหนึ่งสามารถใช้อาร์กิวเมนต์ที่สองเพื่อส่งกลับค่าพิเศษในกรณีที่ไม่พบองค์ประกอบเช่น

In [77]: next((idx for idx, val in np.ndenumerate(a) if val==400),None)

มีฟังก์ชั่นอื่น ๆ ใน NumPy ( argmax, whereและnonzero) ที่สามารถใช้ในการค้นหาองค์ประกอบในอาร์เรย์ แต่พวกเขาทุกคนมีข้อเสียเปรียบที่จะต้องผ่านอาร์เรย์ทั้งหมดเพื่อค้นหาเหตุการณ์ทั้งหมดดังนั้นจึงไม่เหมาะสำหรับการค้นหาองค์ประกอบแรก โปรดทราบว่าwhereและnonzeroส่งคืนอาร์เรย์ดังนั้นคุณต้องเลือกองค์ประกอบแรกเพื่อรับดัชนี

In [71]: np.argmax(a==2)
Out[71]: 2

In [72]: np.where(a==2)
Out[72]: (array([2], dtype=int64),)

In [73]: np.nonzero(a==2)
Out[73]: (array([2], dtype=int64),)

เปรียบเทียบเวลา

เพียงตรวจสอบว่าสำหรับอาร์เรย์ขนาดใหญ่โซลูชันที่ใช้ตัววนซ้ำนั้นเร็วขึ้นเมื่อรายการที่ค้นหาอยู่ที่จุดเริ่มต้นของอาร์เรย์ (ใช้%timeitในเชลล์ IPython):

In [285]: a = np.arange(100000)

In [286]: %timeit next((idx for idx, val in np.ndenumerate(a) if val==0))
100000 loops, best of 3: 17.6 µs per loop

In [287]: %timeit np.argmax(a==0)
1000 loops, best of 3: 254 µs per loop

In [288]: %timeit np.where(a==0)[0][0]
1000 loops, best of 3: 314 µs per loop

นี่เป็นปัญหา NumPy GitHub ที่เปิดอยู่

ดูเพิ่มเติมที่: Numpy: ค้นหาดัชนีค่าแรกอย่างรวดเร็ว


1
ฉันคิดว่าคุณควรรวมเวลาสำหรับกรณีที่เลวร้ายที่สุด (องค์ประกอบสุดท้าย) เพื่อให้ผู้อ่านรู้ว่าเกิดอะไรขึ้นกับพวกเขาในกรณีที่แย่ที่สุดเมื่อพวกเขาใช้วิธีการของคุณ
MSeifert

@MSeifert ฉันไม่สามารถกำหนดเวลาที่เหมาะสมสำหรับโซลูชัน iterator ที่แย่ที่สุดได้ - ฉันจะลบคำตอบนี้จนกว่าฉันจะพบว่ามีอะไรผิดปกติ
user2314737

1
ไม่%timeit next((idx for idx, val in np.ndenumerate(a) if val==99999))ทำงานใช่ไหม หากคุณสงสัยว่าทำไมมันช้ากว่า 1,000 เท่า - เป็นเพราะไพทอนวนไปตามแถวที่มีจำนวนมากนั้นช้ามาก
MSeifert

@MSeifert ไม่ฉันไม่ทราบ แต่ฉันยังงงงวยกับความจริงที่ว่าargmaxและwhereเร็วขึ้นมากในกรณีนี้ (องค์ประกอบการค้นหาที่ส่วนท้ายของอาร์เรย์)
user2314737

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

9

สำหรับอาร์เรย์ที่เรียงลำดับแบบหนึ่งมิติมันจะง่ายกว่าและมีประสิทธิภาพมากกว่า O (บันทึก (n)) เพื่อใช้numpy.searchsortedซึ่งส่งกลับจำนวนเต็ม NumPy (ตำแหน่ง) ตัวอย่างเช่น,

arr = np.array([1, 1, 1, 2, 3, 3, 4])
i = np.searchsorted(arr, 3)

เพียงให้แน่ใจว่าเรียงลำดับแล้ว

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

if arr[i] == 3:
    print("present")
else:
    print("not present")

2
searchsorted ไม่ใช่ nlog (n) เนื่องจากมันไม่ได้เรียงลำดับอาร์เรย์ก่อนทำการค้นหามันจะถือว่าอาร์เรย์อาร์กิวเมนต์นั้นเรียงลำดับแล้ว ตรวจสอบเอกสารของ numpy.searchsorted (ลิงค์ด้านบน)
Alok Nayak

6

หากต้องการทำดัชนีในเกณฑ์ใด ๆ คุณสามารถทำสิ่งต่อไปนี้ได้:

In [1]: from numpy import *
In [2]: x = arange(125).reshape((5,5,5))
In [3]: y = indices(x.shape)
In [4]: locs = y[:,x >= 120] # put whatever you want in place of x >= 120
In [5]: pts = hsplit(locs, len(locs[0]))
In [6]: for pt in pts:
   .....:         print(', '.join(str(p[0]) for p in pt))
4, 4, 0
4, 4, 1
4, 4, 2
4, 4, 3
4, 4, 4

และนี่คือฟังก์ชั่นด่วนในการทำสิ่งที่ list.index () ทำได้ยกเว้นจะไม่เพิ่มข้อยกเว้นหากไม่พบ ระวัง - นี่อาจช้ามากในอาร์เรย์ขนาดใหญ่ คุณอาจจะใช้วิธีนี้ในการแก้ไขอาเรย์ถ้าคุณอยากจะใช้มันเป็นวิธีการ

def ndindex(ndarray, item):
    if len(ndarray.shape) == 1:
        try:
            return [ndarray.tolist().index(item)]
        except:
            pass
    else:
        for i, subarray in enumerate(ndarray):
            try:
                return [i] + ndindex(subarray, item)
            except:
                pass

In [1]: ndindex(x, 103)
Out[1]: [4, 0, 3]

5

สำหรับอาร์เรย์ 1D ฉันขอแนะนำnp.flatnonzero(array == value)[0]ซึ่งเทียบเท่ากับทั้งสองnp.nonzero(array == value)[0][0]และnp.where(array == value)[0][0]เพื่อหลีกเลี่ยงความน่าเกลียดของการถอด tuple แบบ 1 องค์ประกอบ


4

อีกทางเลือกหนึ่งในการเลือกองค์ประกอบแรกจาก np.where () คือการใช้นิพจน์ตัวสร้างพร้อมกับแจกแจงเช่น:

>>> import numpy as np
>>> x = np.arange(100)   # x = array([0, 1, 2, 3, ... 99])
>>> next(i for i, x_i in enumerate(x) if x_i == 2)
2

สำหรับอาร์เรย์สองมิติเราจะทำ:

>>> x = np.arange(100).reshape(10,10)   # x = array([[0, 1, 2,... 9], [10,..19],])
>>> next((i,j) for i, x_i in enumerate(x) 
...            for j, x_ij in enumerate(x_i) if x_ij == 2)
(0, 2)

ข้อดีของวิธีการนี้คือจะหยุดการตรวจสอบองค์ประกอบของอาร์เรย์หลังจากพบคู่แรกในขณะที่ np.where ตรวจสอบองค์ประกอบทั้งหมดเพื่อดูการจับคู่ นิพจน์ตัวสร้างจะเร็วขึ้นหากมีการจับคู่ในช่วงต้นของอาร์เรย์


ในกรณีที่อาจไม่มีการจับคู่ในอาเรย์เลยวิธีนี้ยังช่วยให้คุณระบุค่าทางเลือกได้อย่างสะดวก ถ้าตัวอย่างแรกจะกลับมาเป็นสำรองก็จะกลายเป็นNone next((i for i, x_i in enumerate(x) if x_i == 2), None)
Erlend Magnus Viggen

4

มีการดำเนินงานจำนวนมากใน NumPy ที่อาจนำมารวมกันเพื่อทำสิ่งนี้ให้สำเร็จ สิ่งนี้จะคืนดัชนีขององค์ประกอบเท่ากับรายการ:

numpy.nonzero(array - item)

จากนั้นคุณสามารถนำองค์ประกอบแรกของรายการเพื่อรับองค์ประกอบเดียว


5
จะไม่ให้ดัชนีขององค์ประกอบทั้งหมดที่ไม่เท่ากับรายการหรือไม่
อัตชีวประวัติ

3

numpy_indexedแพคเกจ (ข้อจำกัดความรับผิดชอบผมผู้เขียน) มีเทียบเท่า vectorized ของ list.index สำหรับ numpy.ndarray; นั่นคือ:

sequence_of_arrays = [[0, 1], [1, 2], [-5, 0]]
arrays_to_query = [[-5, 0], [1, 0]]

import numpy_indexed as npi
idx = npi.indices(sequence_of_arrays, arrays_to_query, missing=-1)
print(idx)   # [2, -1]

วิธีการแก้ปัญหานี้มีประสิทธิภาพแบบเวกเตอร์, สรุปทั่วไปเพื่อ ndarrays และมีวิธีการต่างๆในการจัดการกับค่าที่ขาดหายไป


-1

หมายเหตุ: สำหรับรุ่น python 2.7

คุณสามารถใช้ฟังก์ชั่นแลมบ์ดาเพื่อจัดการกับปัญหาและใช้งานได้ทั้งในอาร์เรย์และรายการของ NumPy

your_list = [11, 22, 23, 44, 55]
result = filter(lambda x:your_list[x]>30, range(len(your_list)))
#result: [3, 4]

import numpy as np
your_numpy_array = np.array([11, 22, 23, 44, 55])
result = filter(lambda x:your_numpy_array [x]>30, range(len(your_list)))
#result: [3, 4]

และคุณสามารถใช้

result[0]

เพื่อรับดัชนีแรกขององค์ประกอบที่ถูกกรอง

สำหรับ python 3.6 ให้ใช้

list(result)

แทน

result

ผลลัพธ์นี้ใน<filter object at 0x0000027535294D30>Python 3 (ทดสอบบน Python 3.6.3) มีการอัปเดตสำหรับ Python 3 หรือไม่
ปีเตอร์มอร์เทนเซ่น
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.