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


119

โค้ด Python ต่อไปนี้ดูเหมือนจะยาวมากเมื่อมาจากพื้นหลัง Matlab

>>> a = [1, 2, 3, 1, 2, 3]
>>> [index for index,value in enumerate(a) if value > 2]
[2, 5]

เมื่ออยู่ใน Matlab ฉันสามารถเขียน:

>> a = [1, 2, 3, 1, 2, 3];
>> find(a>2)
ans =
     3     6

มีวิธีการเขียนด้วยมือสั้น ๆ ใน Python หรือไม่หรือฉันแค่ยึดติดกับเวอร์ชันยาว


ขอขอบคุณสำหรับคำแนะนำและคำอธิบายเหตุผลสำหรับไวยากรณ์ของ Python

หลังจากค้นหาสิ่งต่อไปนี้ในเว็บไซต์ numpy ฉันคิดว่าฉันพบวิธีแก้ปัญหาที่ฉันชอบ:

http://docs.scipy.org/doc/numpy/user/basics.indexing.html#boolean-or-mask-index-arrays

การใช้ข้อมูลจากเว็บไซต์นั้นกับปัญหาของฉันข้างต้นจะให้สิ่งต่อไปนี้:

>>> from numpy import array
>>> a = array([1, 2, 3, 1, 2, 3])
>>> b = a>2 
array([False, False, True, False, False, True], dtype=bool)
>>> r = array(range(len(b)))
>>> r(b)
[2, 5]

สิ่งต่อไปนี้ควรใช้งานได้ (แต่ฉันยังไม่มีล่าม Python ในมือเพื่อทดสอบ):

class my_array(numpy.array):
    def find(self, b):
        r = array(range(len(b)))
        return r(b)


>>> a = my_array([1, 2, 3, 1, 2, 3])
>>> a.find(a>2)
[2, 5]

6
แล้วไง[idx for idx in range(len(a)) if a[idx] > 2]? เหตุผลที่ทำใน Python ค่อนข้างอึดอัดเพราะไม่ได้ใช้ดัชนีมากเท่ากับภาษาอื่น ๆ
NullUserException

คำตอบ:


77
  • ใน Python คุณจะไม่ใช้ดัชนีสำหรับสิ่งนี้เลย แต่จัดการกับค่า[value for value in a if value > 2]เท่านั้น- โดยปกติการจัดการกับดัชนีหมายความว่าคุณไม่ได้ทำสิ่งที่ดีที่สุด

  • หากคุณไม่จำเป็นต้องมี API คล้ายกับ Matlab ของคุณจะใช้numpyแพคเกจสำหรับอาร์เรย์หลายมิติและคณิตศาสตร์ตัวเลขในหลามซึ่งเป็นแรงบันดาลใจอย่างมากจาก Matlab คุณจะใช้อาร์เรย์ numpy แทนรายการ

    >>> import numpy
    >>> a = numpy.array([1, 2, 3, 1, 2, 3])
    >>> a
    array([1, 2, 3, 1, 2, 3])
    >>> numpy.where(a > 2)
    (array([2, 5]),)
    >>> a > 2
    array([False, False,  True, False, False,  True], dtype=bool)
    >>> a[numpy.where(a > 2)]
    array([3, 3])
    >>> a[a > 2]
    array([3, 3])

2
คุณมีรายการรายการหนึ่งสำหรับช่วงและอีกรายการสำหรับมุมคุณต้องการกรองค่าของช่วงที่สูงกว่าเกณฑ์บางส่วน คุณจะกรองมุมที่สอดคล้องกับช่วงเหล่านั้นในแบบ "วิธีที่ดีที่สุด" ได้อย่างไร
Mehdi

3
filtered_ranges_and_angles = [(range, angle) for range, angle in zip(ranges, angles) if should_be_kept(range)]
Mike Graham

7
"ใน Python คุณจะไม่ใช้ดัชนีสำหรับสิ่งนี้เลย แต่เพียงแค่จัดการกับค่า" คำสั่งนี้แสดงว่าคุณยังไม่ได้ทำการวิเคราะห์ข้อมูลและการสร้างแบบจำลองแมชชีนเลิร์นนิงเพียงพอ ดัชนีของเทนเซอร์หนึ่งตัวตามเงื่อนไขบางประการถูกใช้เพื่อกรองเทนเซอร์อื่น
horaceT

63

อีกวิธีหนึ่ง:

>>> [i for i in range(len(a)) if a[i] > 2]
[2, 5]

โดยทั่วไปแล้วจำได้ว่าในขณะที่findเป็นฟังก์ชั่นพร้อมสุก comprehensions รายการทั่วไปและวิธีการแก้ปัญหาจึงมีประสิทธิภาพมาก ไม่มีสิ่งใดขัดขวางไม่ให้คุณเขียนfindฟังก์ชันใน Python และใช้งานได้ในภายหลังตามที่คุณต้องการ เช่น:

>>> def find_indices(lst, condition):
...   return [i for i, elem in enumerate(lst) if condition(elem)]
... 
>>> find_indices(a, lambda e: e > 2)
[2, 5]

โปรดทราบว่าฉันใช้รายการที่นี่เพื่อเลียนแบบ Matlab Pythonic จะใช้เครื่องกำเนิดไฟฟ้าและตัววนซ้ำมากกว่า


2
OP สามารถเขียน[i for i,v in enumerate(a) if v > 2]แทนได้
NullUserException

มันไม่สั้นลงมันยาวขึ้น แทนที่indexด้วยiและvalueด้วยvในต้นฉบับและนับอักขระ
agf

@NullUser, agf: คุณพูดถูก แต่ประเด็นหลักคือส่วนที่สอง :)
Eli Bendersky

1
การใช้enumerateมากกว่าrange(len(...))นั้นทั้งแข็งแกร่งและมีประสิทธิภาพมากขึ้น
Mike Graham

1
@ Mike Graham: ฉันเห็นด้วย - จะเปลี่ยนfind_indicesฟังก์ชั่นการใช้งานenumerate
Eli Bendersky


6

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

matchingVals = filter(lambda x : x>2, a)

หรือเขียนส่วนประกอบของรายการของคุณเอง:

matchingVals = [x for x in a if x > 2]

หากคุณต้องการลบออกจากรายการวิธี Pythonic ไม่จำเป็นต้องลบออกจากรายการ แต่เขียนความเข้าใจรายการราวกับว่าคุณกำลังสร้างรายการใหม่และกำหนดกลับในตำแหน่งโดยใช้listvar[:]ทางซ้ายมือ -SIDE:

a[:] = [x for x in a if x <= 2]

Matlab จัดหาfindเนื่องจากโมเดลที่มีอาร์เรย์เป็นศูนย์กลางทำงานโดยการเลือกรายการโดยใช้ดัชนีอาร์เรย์ คุณสามารถทำได้ใน Python แน่นอน แต่ยิ่ง Pythonic ใช้ตัววนซ้ำและเครื่องกำเนิดไฟฟ้ามากเท่าที่ @EliBendersky กล่าวไว้แล้ว


พอลฉันยังไม่พบความต้องการนี้ในสคริปต์ / ฟังก์ชัน / คลาส เป็นการทดสอบเชิงโต้ตอบของชั้นเรียนที่ฉันกำลังเขียนมากกว่า
ลี

@ ไมค์ - ขอบคุณสำหรับการแก้ไข แต่จริงๆแล้วฉันหมายถึงa[:] = ...- ดูคำตอบอเล็กซ์เทลของคำถามนี้stackoverflow.com/questions/1352885/...
PaulMcG

@ พอลฉันคิดว่า (และหวังว่า!) คุณไม่ได้หมายถึงจริงๆจากคำอธิบายของคุณว่าคุณกำลังจะ "สร้างรายการใหม่"; ฉันพบว่าโปรแกรมมีแนวโน้มที่จะเข้าใจและดูแลรักษาได้ง่ายขึ้นเมื่อพวกเขากลายพันธุ์ข้อมูลที่มีอยู่อย่าง จำกัด ไม่ว่าในกรณีใด ๆ ขออภัยที่เกินขั้นตอน - คุณควรจะแก้ไขโพสต์ของคุณกลับไปเป็นแบบที่คุณต้องการได้อย่างแน่นอน
Mike Graham

6

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

คุณสามารถกำหนดฟังก์ชันด้วยตนเองซึ่งมีฟังก์ชันดังกล่าว:

def indices(list, filtr=lambda x: bool(x)):
    return [i for i,x in enumerate(list) if filtr(x)]

print(indices([1,0,3,5,1], lambda x: x==1))

ผลตอบแทน: [0, 4]

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

class MyList(list):
    def __init__(self, *args):
        list.__init__(self, *args)
    def indices(self, filtr=lambda x: bool(x)):
        return [i for i,x in enumerate(self) if filtr(x)]

my_list = MyList([1,0,3,5,1])
my_list.indices(lambda x: x==1)

ฉันอธิบายเพิ่มเติมเกี่ยวกับหัวข้อนั้นที่นี่: http://tinyurl.com/jajrr87

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.