Python: Wybierz podzbiór z listy na podstawie zestawu indeksów

99

Mam kilka list mających tę samą liczbę wpisów (każda z określeniem właściwości obiektu):

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

i lista z flagami o tej samej długości

good_objects = [True, False, False, True]

(który można łatwo zastąpić równoważną listą indeksów:

good_indices = [0, 3]

Jaki jest najłatwiejszy sposób generowania nowych list property_asel, property_bsel... które zawierają tylko wartości wskazane przez Truewpisy lub indeksy?

property_asel = [545., 33.]
property_bsel = [ 1.2, 0.3]
fuenfundachtzig
źródło

Odpowiedzi:

128

Możesz po prostu użyć rozumienia list :

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

lub

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

Ta ostatnia jest szybsza, ponieważ jest ich mniej good_indicesniż długość property_a, przy założeniu, że good_indicessą wstępnie obliczane zamiast generowane w locie.


Edycja : pierwsza opcja jest równoważna itertools.compressdostępnej od wersji Python 2.7 / 3.1. Zobacz odpowiedź @Gary Kerr .

property_asel = list(itertools.compress(property_a, good_objects))
kennytm
źródło
1
@fuen: Tak. Powoduje dużo w Pythonie 2 ( zamiast tego użyj itertools.izip ), a nie w Pythonie 3. Dzieje się tak, ponieważ zipw Pythonie 2 utworzy nową listę, ale w Pythonie 3 zwróci (leniwy) generator.
kennytm
OK, więc powinienem trzymać się twojej drugiej propozycji, ponieważ stanowi ona centralną część mojego kodu.
fuenfundachtzig
4
@ 85: dlaczego martwisz się o wydajność? Napisz, co musisz zrobić, jeśli jest wolny, a następnie przetestuj, aby znaleźć wąskie gardła.
Gary Kerr,
1
@PreludeAndFugue: Jeśli istnieją dwie równoważne opcje, dobrze jest wiedzieć, która z nich jest szybsza, i od razu jej użyć.
fuenfundachtzig
1
Możesz po prostu użyć tego from itertools import izipi użyć zamiast zipw pierwszym przykładzie. To tworzy iterator, taki sam jak Python 3.
Chris B.
28

Widzę 2 opcje.

  1. Korzystanie 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. Korzystanie z listy i spakowanie jej:

    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
źródło
2
Używanie Numpy to dobra sugestia, ponieważ wydaje się, że OP chce przechowywać liczby na listach. Dwuwymiarowa tablica byłaby jeszcze lepsza.
Philipp,
Jest to również dobra sugestia, ponieważ będzie to składnia bardzo dobrze znana użytkownikom języka R, gdzie ten rodzaj selekcji jest bardzo potężny, zwłaszcza gdy jest zagnieżdżony i / lub wielowymiarowy.
Thomas Browne
1
[property_b[i] for i in good_indices]jest dobry do używania beznumpy
Ilya Rusin
15

Użyj wbudowanej funkcji zip

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

EDYTOWAĆ

Wystarczy spojrzeć na nowe funkcje wersji 2.7. W module itertools znajduje się teraz funkcja podobna do powyższego kodu.

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

itertools.compress('ABCDEF', [1,0,1,0,1,1]) =>
  A, C, E, F
Gary Kerr
źródło
1
Jestem rozczarowany użyciem itertools.compresstutaj. Zrozumienie listy jest dużo bardziej czytelne, bez konieczności odkrywania tego, co robi kompres.
PaulMcG,
5
Hm, uważam, że kod używający kompresji jest dużo bardziej czytelny :) Może jestem stronniczy, bo robi dokładnie to, czego chcę.
fuenfundachtzig
Dlaczego nie podasz przykładu itertools.compresszamiast kopiować, wklejając przykład dokumentacji?
Nicolas Gervais
8

Zakładając, że masz tylko listę pozycji i listę prawdziwych / wymaganych wskaźników, powinno to być najszybsze:

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

Oznacza to, że wybór właściwości wykona tylko tyle rund, ile jest prawdziwych / wymaganych wskaźników. Jeśli masz wiele list właściwości, które są zgodne z regułami pojedynczej listy tagów (prawda / fałsz), możesz utworzyć listę indeksów, korzystając z tych samych zasad rozumienia list:

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

To iteruje przez każdy element w good_objects (pamiętając jego indeks z enumerate) i zwraca tylko indeksy, w których element jest prawdziwy.


Dla każdego, kto nie rozumie listy, oto angielska proza ​​z kodem wyróżnionym pogrubioną czcionką:

notować indeksu dla każdej grupy indeks, przedmiot , który istnieje w na wyliczenie z dobrymi obiektami , jeśli (gdzie) pozycja jest prawda

Eyrofire
źródło
-1

Języki Matlab i Scilab oferują prostszą i bardziej elegancką składnię niż Python dla pytania, które zadajesz, więc myślę, że najlepsze, co możesz zrobić, to naśladować Matlab / Scilab za pomocą pakietu Numpy w Pythonie. W ten sposób rozwiązanie problemu jest bardzo zwięzłe i eleganckie:

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 próbuje naśladować Matlab / Scilab, ale ma to swoją cenę: musisz zadeklarować każdą listę ze słowem kluczowym "array", co spowoduje przeciążenie twojego skryptu (ten problem nie istnieje w Matlab / Scilab). Zauważ, że to rozwiązanie jest ograniczone do tablic liczb, co ma miejsce w twoim przykładzie.

FredAndre
źródło
5
Nigdzie w pytaniu nie wspomina o NumPy - nie ma potrzeby wyrażania opinii na temat NumPy vs Matlab. Listy Pythona to nie to samo, co tablice NumPy, nawet jeśli obie z grubsza odpowiadają wektorom. (Listy Pythona są jak tablice komórek Matlaba - każdy element może mieć inny typ danych. Tablice NumPy są bardziej ograniczone, aby umożliwić pewne optymalizacje). Możesz uzyskać podobną składnię do swojego przykładu za pomocą wbudowanej filterbiblioteki Pythona lub biblioteki zewnętrznej pandas. Jeśli zamierzasz zamienić języki, możesz również spróbować R, ale nie o to chodzi w pytaniu .
Livius