Python: Pilih subset dari daftar berdasarkan set indeks

99

Saya memiliki beberapa daftar yang memiliki jumlah entri yang sama (masing-masing menentukan properti objek):

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

dan daftar dengan bendera dengan panjang yang sama

good_objects = [True, False, False, True]

(yang dapat dengan mudah diganti dengan daftar indeks yang setara:

good_indices = [0, 3]

Apa cara termudah untuk menghasilkan daftar baru property_asel, property_bsel, ... yang hanya berisi nilai-nilai yang ditunjukkan baik oleh Trueentri atau indeks?

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

Jawaban:

128

Anda bisa menggunakan pemahaman daftar :

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

atau

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

Yang terakhir lebih cepat karena good_indicespanjangnya lebih sedikit property_a, dengan asumsi good_indicesdihitung sebelumnya daripada dihasilkan secara on-the-fly.


Edit : Opsi pertama setara dengan yang itertools.compresstersedia sejak Python 2.7 / 3.1. Lihat jawaban @Gary Kerr .

property_asel = list(itertools.compress(property_a, good_objects))
kennytm
sumber
1
@fuen: Ya. Banyak penyebab pada Python 2 (gunakan itertools.izip sebagai gantinya), tidak terlalu banyak pada Python 3. Ini karena zipdi Python 2 akan membuat daftar baru, tetapi di Python 3 itu hanya akan mengembalikan generator (malas).
kennytm
Oke, jadi saya harus tetap berpegang pada proposal kedua Anda, karena ini merupakan bagian sentral dari kode saya.
fuenfundachtzig
4
@ 85: mengapa Anda mengkhawatirkan kinerja? Tulis apa yang harus Anda lakukan, jika lambat, kemudian uji untuk menemukan kemacetan.
Gary Kerr
1
@PreludeAndFugue: Jika ada dua opsi yang setara, ada baiknya untuk mengetahui mana yang lebih cepat, dan segera gunakan yang itu.
fuenfundachtzig
1
Anda bisa menggunakan from itertools import izipdan menggunakannya sebagai ganti zipcontoh pertama. Itu menciptakan sebuah iterator, sama seperti Python 3.
Chris B.
28

Saya melihat 2 opsi.

  1. Menggunakan 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. Menggunakan pemahaman daftar dan zip itu:

    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]
    
Wolph
sumber
2
Menggunakan Numpy adalah saran yang bagus karena OP sepertinya ingin menyimpan nomor dalam daftar. Array dua dimensi akan lebih baik.
Philipp
Ini juga merupakan saran yang bagus karena ini akan menjadi sintaks yang sangat familiar bagi pengguna R, di mana pilihan semacam ini sangat berguna, terutama saat bersarang dan / atau multidimensi.
Thomas Browne
[property_b[i] for i in good_indices]bagus untuk digunakan tanpanumpy
Ilya Rusin
15

Gunakan zip fungsi bawaan

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

EDIT

Hanya melihat fitur baru 2.7. Sekarang ada fungsi dalam modul itertools yang mirip dengan kode di atas.

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

itertools.compress('ABCDEF', [1,0,1,0,1,1]) =>
  A, C, E, F
Gary Kerr
sumber
1
Saya kecewa dengan penggunaan di itertools.compresssini. Pemahaman daftar jauh lebih mudah dibaca, tanpa harus menggali apa yang dilakukan kompres.
PaulMcG
5
Hm, menurut saya kode yang menggunakan kompres jauh lebih mudah dibaca :) Mungkin saya bias, karena kode tersebut melakukan apa yang saya inginkan.
fuenfundachtzig
Mengapa Anda tidak memberikan contoh itertools.compressalih - alih menyalin menempel contoh dokumentasi?
Nicolas Gervais
8

Dengan asumsi Anda hanya memiliki daftar item dan daftar indeks benar / diperlukan, ini harus menjadi yang tercepat:

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

Ini berarti pemilihan properti hanya akan melakukan putaran sebanyak ada indeks yang benar / diperlukan. Jika Anda memiliki banyak daftar properti yang mengikuti aturan daftar tag tunggal (benar / salah), Anda dapat membuat daftar indeks menggunakan prinsip pemahaman daftar yang sama:

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

Ini mengulangi setiap item dalam good_objects (sambil mengingat indeksnya dengan enumerate) dan hanya mengembalikan indeks di mana item tersebut benar.


Bagi siapa pun yang tidak memahami daftar, berikut adalah versi prosa bahasa Inggris dengan kode yang disorot dalam huruf tebal:

daftar indeks untuk setiap kelompok indeks, barang yang ada di sebuah enumerasi dari benda-benda yang baik , jika (di mana) yang item yang Benar

Eyrofire
sumber
-1

Bahasa Matlab dan Scilab menawarkan sintaks yang lebih sederhana dan lebih elegan daripada Python untuk pertanyaan yang Anda ajukan, jadi menurut saya yang terbaik yang dapat Anda lakukan adalah meniru Matlab / Scilab dengan menggunakan paket Numpy dengan Python. Dengan melakukan ini, solusi untuk masalah Anda sangat ringkas dan elegan:

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 mencoba meniru Matlab / Scilab tetapi ada biayanya: Anda perlu mendeklarasikan setiap daftar dengan kata kunci "array", sesuatu yang akan membebani skrip Anda (masalah ini tidak ada pada Matlab / Scilab). Perhatikan bahwa solusi ini terbatas pada larik angka, yang merupakan kasus dalam contoh Anda.

FredAndre
sumber
5
Tidak ada dalam pertanyaan apakah dia menyebutkan NumPy - tidak perlu mengungkapkan pendapat Anda tentang NumPy vs Matlab. Daftar Python tidak sama dengan array NumPy, meskipun keduanya secara kasar sesuai dengan vektor. (Daftar Python seperti array sel Matlab - setiap elemen dapat memiliki tipe data yang berbeda. Array NumPy lebih dibatasi untuk mengaktifkan pengoptimalan tertentu). Anda bisa mendapatkan sintaks yang mirip dengan contoh Anda melalui bawaan Python filteratau pustaka eksternal pandas. Jika Anda akan menukar bahasa, Anda juga dapat mencoba R, tetapi bukan itu pertanyaannya .
Livius