การกรองรายการตามรายการบูลีน


127

ฉันมีรายการของค่าที่ฉันต้องการกรองตามค่าในรายการบูลีน:

list_a = [1, 2, 4, 6]
filter = [True, False, True, False]

ฉันสร้างรายการที่กรองใหม่ด้วยบรรทัดต่อไปนี้:

filtered_list = [i for indx,i in enumerate(list_a) if filter[indx] == True]

ซึ่งส่งผลให้:

print filtered_list
[1,4]

สายใช้งานได้ แต่ดูเหมือน (สำหรับฉัน) มากเกินไปและฉันก็สงสัยว่ามีวิธีที่ง่ายกว่าในการบรรลุเป้าหมายเดียวกันหรือไม่


คำแนะนำ

สรุปคำแนะนำที่ดีสองประการในคำตอบด้านล่าง:

1- อย่าตั้งชื่อรายการfilterเหมือนที่ฉันทำเพราะเป็นฟังก์ชันในตัว

2- อย่าเปรียบเทียบสิ่งต่างๆกับสิ่งที่Trueฉันทำif filter[idx]==True..เพราะมันไม่จำเป็น แค่ใช้if filter[idx]ก็เพียงพอแล้ว


3
เพียง FYI นี้เป็นคู่ขนานทั่วไปคอมพิวเตอร์ดั้งเดิมที่เรียกว่ากระแสการบดอัด (เรียกว่า 'ดั้งเดิม' ไม่ใช่เพราะมันเรียบง่าย แต่เป็นเพราะมันถูกใช้เป็นส่วนประกอบสำหรับอัลกอริธึมแบบขนานอื่น ๆ )
BlueRaja - Danny Pflughoeft

2
บางบันทึกสไตล์: if filter[indx] == Trueไม่ได้ใช้==ถ้าคุณต้องการที่จะตรวจสอบตัวตนกับการใช้งานTrue อย่างไรก็ตามในกรณีนี้การเปรียบเทียบทั้งไม่มีประโยชน์ที่คุณก็สามารถใช้is if filter[indx]สุดท้าย: อย่าใช้ชื่อบิวท์อินเป็นชื่อตัวแปร / โมดูล (ฉันหมายถึงชื่อfilter) ใช้สิ่งที่ต้องการincludedเพื่อให้ifอ่านได้ดี ( if included[indx])
Bakuriu

คำตอบ:


184

คุณกำลังมองหาitertools.compress:

>>> from itertools import compress
>>> list_a = [1, 2, 4, 6]
>>> fil = [True, False, True, False]
>>> list(compress(list_a, fil))
[1, 4]

การเปรียบเทียบเวลา (py3.x):

>>> list_a = [1, 2, 4, 6]
>>> fil = [True, False, True, False]
>>> %timeit list(compress(list_a, fil))
100000 loops, best of 3: 2.58 us per loop
>>> %timeit [i for (i, v) in zip(list_a, fil) if v]  #winner
100000 loops, best of 3: 1.98 us per loop

>>> list_a = [1, 2, 4, 6]*100
>>> fil = [True, False, True, False]*100
>>> %timeit list(compress(list_a, fil))              #winner
10000 loops, best of 3: 24.3 us per loop
>>> %timeit [i for (i, v) in zip(list_a, fil) if v]
10000 loops, best of 3: 82 us per loop

>>> list_a = [1, 2, 4, 6]*10000
>>> fil = [True, False, True, False]*10000
>>> %timeit list(compress(list_a, fil))              #winner
1000 loops, best of 3: 1.66 ms per loop
>>> %timeit [i for (i, v) in zip(list_a, fil) if v] 
100 loops, best of 3: 7.65 ms per loop

อย่าใช้filterเป็นชื่อตัวแปรเป็นฟังก์ชันในตัว


@Mehdi ฉันพบว่าวิธี Matlab ไม่ได้ใช้งานง่าย แต่ฉันคิดว่ามันขึ้นอยู่กับสิ่งที่คุณคุ้นเคย
Ian Goldby

ฉันจะเลือกได้[2, 6]อย่างไร?
Florent

ฉันเข้าใจแล้วlist(compress(list_a, [not i for i in fill]))ควรกลับ[2, 6]
Florent

42

ชอบมาก:

filtered_list = [i for (i, v) in zip(list_a, filter) if v]

การใช้zipเป็นวิธีpythonicในการวนซ้ำหลาย ๆ ลำดับแบบขนานโดยไม่จำเป็นต้องจัดทำดัชนีใด ๆ สิ่งนี้ถือว่าทั้งสองลำดับมีความยาวเท่ากัน (ซิปจะหยุดหลังจากหมดเวลาสั้นที่สุด) การใช้itertoolsสำหรับกรณีง่ายๆเช่นนี้เป็นเรื่องที่มากเกินไป ...

สิ่งหนึ่งที่คุณทำในตัวอย่างที่คุณควรหยุดทำคือการเปรียบเทียบสิ่งต่าง ๆ กับ True ซึ่งโดยปกติแล้วไม่จำเป็น แทนที่จะคุณก็สามารถเขียนif filter[idx]==True: ...if filter[idx]: ...


40

ด้วย numpy:

In [128]: list_a = np.array([1, 2, 4, 6])
In [129]: filter = np.array([True, False, True, False])
In [130]: list_a[filter]

Out[130]: array([1, 4])

หรือดูคำตอบของ Alex Szatmary หาก list_a สามารถเป็นอาร์เรย์ numpy แต่ไม่กรอง

Numpy มักจะช่วยเพิ่มความเร็วให้คุณได้มากเช่นกัน

In [133]: list_a = [1, 2, 4, 6]*10000
In [134]: fil = [True, False, True, False]*10000
In [135]: list_a_np = np.array(list_a)
In [136]: fil_np = np.array(fil)

In [139]: %timeit list(itertools.compress(list_a, fil))
1000 loops, best of 3: 625 us per loop

In [140]: %timeit list_a_np[fil_np]
10000 loops, best of 3: 173 us per loop

จุดดีฉันชอบใช้NumPyมากกว่าlistที่เป็นไปได้ แต่ถ้าคุณจำเป็นต้องใช้listต่อไปคุณมี (ใช้NumPyวิธีแก้ปัญหา) สร้างnp.arrayจากทั้งสองรายการใช้การสร้างดัชนีแบบบูลีนและในที่สุดก็แปลงอาร์เรย์กลับเป็นรายการด้วยtolist()วิธีการ เพื่อความแม่นยำคุณควรรวมการสร้างวัตถุเหล่านั้นไว้ในการเปรียบเทียบเวลา จากนั้นการใช้itertools.compressจะยังคงเป็นวิธีแก้ปัญหาที่เร็วที่สุด
Nerxis

17

ในการทำสิ่งนี้โดยใช้ numpy กล่าวคือถ้าคุณมีอาร์เรย์aแทนที่จะเป็นlist_a:

a = np.array([1, 2, 4, 6])
my_filter = np.array([True, False, True, False], dtype=bool)
a[my_filter]
> array([1, 4])

3
หากคุณเปลี่ยน my_filter เป็นอาร์เรย์บูลีนคุณสามารถใช้การสร้างดัชนีบูลีนโดยตรงได้โดยไม่จำเป็นต้องใช้where.
Bas Swinckels


-1

ด้วย python 3 คุณสามารถใช้list_a[filter]เพื่อรับTrueค่า หากต้องการรับFalseค่าให้ใช้list_a[~filter]

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