Python: Dizin kümesine göre listeden alt küme seçin


101

Aynı sayıda girişe sahip birkaç listem var (her biri bir nesne özelliğini belirten):

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

ve aynı uzunlukta bayraklar içeren liste

good_objects = [True, False, False, True]

(eşdeğer bir dizin listesi ile kolayca ikame edilebilir:

good_indices = [0, 3]

Yeni liste oluşturmak için en kolay yolu nedir property_asel, property_bsel... yalnızca değerleri yoluyla gösterilen içeren Truegirdileri veya endeksleri?

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

Yanıtlar:


130

Sadece liste anlamayı kullanabilirsiniz :

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

veya

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

İkincisi daha hızlıdır, çünkü anında üretilmek yerine önceden hesaplandığı varsayılarak good_indicesuzunluğundan daha azdır .property_agood_indices


Düzenleme : İlk seçenek itertools.compressPython 2.7 / 3.1'den beri mevcut olana eşdeğerdir . Bkz @Gary Kerr cevabını.

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

1
@fuen: Evet. Python 2'de çok fazla neden olur ( bunun yerine itertools.izip kullanın), Python 3'te çok fazla değil. Bunun nedeni zipPython 2'de yeni bir liste oluşturacak, ancak Python 3'te sadece bir (tembel) oluşturucu döndürecektir.
kennytm

Tamam, öyleyse 2. teklifinize bağlı kalmalıyım, çünkü bu benim kodumun ana bölümünü oluşturuyor.
fuenfundachtzig

4
@ 85: performans konusunda neden endişeleniyorsunuz? Yapmanız gerekeni yazın, eğer yavaşsa, darboğazları bulmak için test edin.
Gary Kerr

1
@PreludeAndFugue: İki eşdeğer seçenek varsa, hangisinin daha hızlı olduğunu bilmek ve onu hemen kullanmak iyidir.
fuenfundachtzig

1
İlk örnek from itertools import izipyerine bunu kullanabilir ve kullanabilirsiniz zip. Yani Python 3. aynı bir yineleyici oluşturur
Chris B.

28

2 seçenek görüyorum.

  1. Numpy kullanarak:

    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. Bir liste anlayışı kullanarak ve sıkıştırın:

    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'yi kullanmak iyi bir öneridir, çünkü OP sayıları listelerde saklamak istiyor gibi görünüyor. İki boyutlu bir dizi daha da iyi olurdu.
Philipp

Bu aynı zamanda iyi bir öneri çünkü bu, özellikle iç içe ve / veya çok boyutlu olduğunda bu tür bir seçimin çok güçlü olduğu R kullanıcıları için çok tanıdık bir sözdizimi olacaktır.
Thomas Browne

1
[property_b[i] for i in good_indices]olmadan kullanmak için iyi birnumpy
Ilya Rusin

15

Yerleşik işlev zipini kullanın

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

DÜZENLE

2.7'nin yeni özelliklerine bakıyorum. Artık itertools modülünde yukarıdaki koda benzer bir işlev vardır.

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

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

1
itertools.compressBurada kullanım beni çok etkiledi . Sıkıştırmanın ne yaptığını anlamak zorunda kalmadan liste anlayışı çok daha okunabilir.
PaulMcG

5
Hm, sıkıştırma kullanarak kodu çok daha okunaklı buluyorum :) Belki önyargılıyım, çünkü tam olarak istediğimi yapıyor.
fuenfundachtzig

itertools.compressBelgeleme örneğini kopyalayıp yapıştırmak yerine neden bir örnek vermiyorsunuz ?
Nicolas Gervais

8

Yalnızca öğelerin listesine ve doğru / gerekli dizinlerin listesine sahip olduğunuzu varsayarsak, bu en hızlı olmalıdır:

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

Bu, özellik seçiminin yalnızca doğru / gerekli endeksler olduğu kadar çok tur yapacağı anlamına gelir. Tek bir etiket (doğru / yanlış) listesinin kurallarına uyan çok sayıda özellik listeniz varsa, aynı liste anlama ilkelerini kullanarak bir indeks listesi oluşturabilirsiniz:

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

Bu, good_objects'teki her öğeyi yineler (numaralandırmayla dizinini hatırlarken) ve yalnızca öğenin doğru olduğu dizinleri döndürür.


Listeyi anlamayanlar için, burada kodun kalın olarak vurgulanmış olduğu bir İngilizce düzyazı versiyonu verilmiştir:

Listeye için endeksini her grup indeksi, madde var içinde bir numaralandırma arasında iyi nesneler , eğer (burada) öğesi True


-1

Matlab ve Scilab dilleri, sorduğunuz soru için Python'dan daha basit ve daha zarif bir sözdizimi sunar, bu yüzden yapabileceğiniz en iyi şeyin Python'da Numpy paketini kullanarak Matlab / Scilab'ı taklit etmek olduğunu düşünüyorum. Bunu yaparak probleminizin çözümü çok özlü ve zariftir:

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'ı taklit etmeye çalışır, ancak bunun bir bedeli vardır: Her listeyi "dizi" anahtar kelimesiyle bildirmeniz gerekir, bu, komut dosyanızı aşırı yükleyecektir (bu sorun Matlab / Scilab ile mevcut değildir). Bu çözümün, örneğinizde olduğu gibi sayı dizileriyle sınırlı olduğunu unutmayın.


5
Sorunun hiçbir yerinde NumPy'den bahsetmiyor - NumPy ve Matlab hakkında fikrinizi ifade etmenize gerek yok. Python listeleri, her ikisi de kabaca vektörlere karşılık gelse bile NumPy dizileriyle aynı şey değildir . (Python listeleri Matlab hücre dizileri gibidir - her eleman farklı bir veri türüne sahip olabilir. NumPy dizileri belirli optimizasyonları etkinleştirmek için daha kısıtlıdır). Python'un yerleşik filterveya harici kitaplığı aracılığıyla örneğinize benzer sözdizimi elde edebilirsiniz pandas. Dilleri değiş tokuş edecekseniz, R'yi de deneyebilirsiniz, ancak sorunun sorduğu bu değil .
Livius
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.