Python: เลือกชุดย่อยจากรายการตามชุดดัชนี


99

ฉันมีหลายรายการที่มีจำนวนรายการเท่ากันทั้งหมด (แต่ละรายการระบุคุณสมบัติของวัตถุ):

property_a = [545., 656., 5.4, 33.]
property_b = [ 1.2,  1.3, 2.3, 0.3]
...

และแสดงรายการด้วยธงที่มีความยาวเท่ากัน

good_objects = [True, False, False, True]

(ซึ่งสามารถแทนที่ได้อย่างง่ายดายด้วยรายการดัชนีที่เทียบเท่า:

good_indices = [0, 3]

เป็นวิธีที่ง่ายที่สุดในการสร้างรายการใหม่อะไรproperty_asel, property_bsel... ซึ่งมีเฉพาะค่าที่ระบุอย่างใดอย่างหนึ่งตามTrueรายการหรือดัชนี?

property_asel = [545., 33.]
property_bsel = [ 1.2, 0.3]

คำตอบ:


128

คุณสามารถใช้ความเข้าใจรายการ :

property_asel = [val for is_good, val in zip(good_objects, property_a) if is_good]

หรือ

property_asel = [property_a[i] for i in good_indices]

อันหลังเร็วกว่าเนื่องจากมีgood_indicesความยาวน้อยกว่าproperty_aโดยสมมติว่าgood_indicesมีการคำนวณล่วงหน้าแทนที่จะสร้างขึ้นทันที


แก้ไข : ตัวเลือกแรกเทียบเท่ากับที่itertools.compressมีให้ตั้งแต่ Python 2.7 / 3.1 ดูคำตอบของ@Gary Kerr

property_asel = list(itertools.compress(property_a, good_objects))

1
@fuen: ครับ ทำให้เกิดจำนวนมากบน Python 2 (ใช้itertools.izipแทน) ไม่มากนักใน Python 3 เนื่องจากzipใน Python 2 จะสร้างรายการใหม่ แต่ใน Python 3 จะส่งคืนเครื่องกำเนิดไฟฟ้า (lazy)
kennytm

ตกลงดังนั้นฉันควรยึดติดกับข้อเสนอที่ 2 ของคุณเพราะสิ่งนี้ถือเป็นส่วนสำคัญของรหัสของฉัน
fuenfundachtzig

4
@ 85: ทำไมคุณถึงกังวลเกี่ยวกับประสิทธิภาพ? เขียนสิ่งที่คุณต้องทำถ้าช้าแล้วทดสอบเพื่อหาคอขวด
Gary Kerr

1
@PreludeAndFugue: หากมีสองตัวเลือกที่เทียบเท่ากันคุณควรรู้ว่าตัวเลือกใดเร็วกว่าและใช้ตัวเลือกนั้นทันที
fuenfundachtzig

1
คุณสามารถใช้from itertools import izipและใช้แทนzipในตัวอย่างแรกได้ ที่สร้างตัววนซ้ำเช่นเดียวกับ Python 3
Chris B.

28

ฉันเห็น 2 ตัวเลือก

  1. ใช้ numpy:

    property_a = numpy.array([545., 656., 5.4, 33.])
    property_b = numpy.array([ 1.2,  1.3, 2.3, 0.3])
    good_objects = [True, False, False, True]
    good_indices = [0, 3]
    property_asel = property_a[good_objects]
    property_bsel = property_b[good_indices]
    
  2. ใช้ความเข้าใจในรายการและซิป:

    property_a = [545., 656., 5.4, 33.]
    property_b = [ 1.2,  1.3, 2.3, 0.3]
    good_objects = [True, False, False, True]
    good_indices = [0, 3]
    property_asel = [x for x, y in zip(property_a, good_objects) if y]
    property_bsel = [property_b[i] for i in good_indices]
    

2
การใช้ Numpy เป็นคำแนะนำที่ดีเนื่องจาก OP ดูเหมือนต้องการเก็บตัวเลขไว้ในรายการ อาร์เรย์สองมิติจะดียิ่งขึ้น
Philipp

นอกจากนี้ยังเป็นคำแนะนำที่ดีเนื่องจากจะเป็นไวยากรณ์ที่คุ้นเคยสำหรับผู้ใช้ R ซึ่งการเลือกประเภทนี้มีประสิทธิภาพมากโดยเฉพาะเมื่อซ้อนกันและ / หรือหลายมิติ
Thomas Browne

1
[property_b[i] for i in good_indices]เหมาะสำหรับใช้โดยไม่ต้องใช้numpy
Ilya Rusin

15

ใช้ซิปฟังก์ชันในตัว

property_asel = [a for (a, truth) in zip(property_a, good_objects) if truth]

แก้ไข

เพียงแค่ดูคุณสมบัติใหม่ของ 2.7 ขณะนี้มีฟังก์ชันในโมดูล itertools ซึ่งคล้ายกับโค้ดด้านบน

http://docs.python.org/library/itertools.html#itertools.compress

itertools.compress('ABCDEF', [1,0,1,0,1,1]) =>
  A, C, E, F

1
ฉันรู้สึกแย่กับการใช้itertools.compressที่นี่ ความเข้าใจรายการคือไกลมากขึ้นที่สามารถอ่านได้โดยไม่ต้องขุดขึ้นมาสิ่งที่บีบอัดห่าจะทำ
PaulMcG

5
อืมฉันพบโค้ดที่ใช้การบีบอัดอ่านได้มากกว่า :) บางทีฉันอาจจะลำเอียงเพราะมันทำในสิ่งที่ฉันต้องการ
fuenfundachtzig

ทำไมคุณไม่ให้ตัวอย่างitertools.compressแทนที่จะคัดลอกวางตัวอย่างเอกสาร
Nicolas Gervais

8

สมมติว่าคุณมีเฉพาะรายการและรายการดัชนีจริง / ที่จำเป็นซึ่งควรเร็วที่สุด:

property_asel = [ property_a[index] for index in good_indices ]

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

good_indices = [ index for index, item in enumerate(good_objects) if item ]

สิ่งนี้จะวนซ้ำผ่านแต่ละรายการใน good_objects (ในขณะที่จดจำดัชนีพร้อมแจงนับ) และส่งกลับเฉพาะดัชนีที่รายการนั้นเป็นจริง


สำหรับใครก็ตามที่ไม่เข้าใจรายชื่อนี่คือเวอร์ชันร้อยแก้วภาษาอังกฤษที่เน้นรหัสเป็นตัวหนา:

รายการดัชนีสำหรับกลุ่มทุกดัชนีรายการที่มีอยู่ในแจงนับของวัตถุที่ดี , ถ้า (ที่) เดอะรายการเป็น True


-1

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

from numpy import *
property_a = array([545., 656., 5.4, 33.])
property_b = array([ 1.2,  1.3, 2.3, 0.3])
good_objects = [True, False, False, True]
good_indices = [0, 3]
property_asel = property_a[good_objects]
property_bsel = property_b[good_indices]

Numpy พยายามเลียนแบบ Matlab / Scilab แต่มีค่าใช้จ่าย: คุณต้องประกาศทุกรายการด้วยคีย์เวิร์ด "array" ซึ่งจะทำให้สคริปต์ของคุณทำงานหนักเกินไป (ปัญหานี้ไม่มีใน Matlab / Scilab) โปรดทราบว่าโซลูชันนี้ จำกัด เฉพาะอาร์เรย์ของจำนวนซึ่งเป็นกรณีในตัวอย่างของคุณ


5
เขาพูดถึง NumPy ที่ไหนในคำถาม - ไม่จำเป็นต้องแสดงความคิดเห็นของคุณเกี่ยวกับ NumPy vs Matlab รายการ Python ไม่เหมือนกับอาร์เรย์ NumPy แม้ว่าทั้งคู่จะสอดคล้องกับเวกเตอร์โดยประมาณก็ตาม (รายการ Python เปรียบเสมือนอาร์เรย์ของเซลล์ Matlab - แต่ละองค์ประกอบสามารถมีประเภทข้อมูลที่แตกต่างกันอาร์เรย์ NumPy ถูก จำกัด มากขึ้นเพื่อเปิดใช้งานการเพิ่มประสิทธิภาพบางอย่าง) คุณจะได้รับไวยากรณ์คล้ายกับตัวอย่างของคุณผ่านทางงูใหญ่ที่สร้างขึ้นในหรือห้องสมุดภายนอกfilter pandasหากคุณกำลังจะเป็นภาษาแลกเปลี่ยนนอกจากนี้คุณยังสามารถลอง R แต่นั่นไม่ใช่สิ่งที่เป็นคำถามที่ถาม
Livius
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.