Python: seleziona un sottoinsieme dall'elenco in base al set di indici


98

Ho diversi elenchi con lo stesso numero di voci (ciascuno che specifica una proprietà dell'oggetto):

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

ed elenco con flag della stessa lunghezza

good_objects = [True, False, False, True]

(che potrebbe essere facilmente sostituito con un elenco di indici equivalente:

good_indices = [0, 3]

Qual è il modo più semplice per generare nuove liste property_asel, property_bsel, ..., che contengono solo i valori indicati sia per le Truevoci o gli indici?

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

Risposte:


126

Potresti semplicemente usare la comprensione dell'elenco :

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

o

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

Quest'ultimo è più veloce perché ce ne sono meno good_indicesdella lunghezza di property_a, supponendo che good_indicessiano precalcolati anziché generati al volo.


Modifica : la prima opzione è equivalente a quella itertools.compressdisponibile da Python 2.7 / 3.1. Vedi la risposta di @Gary Kerr .

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

1
@fuen: Sì. Causa molto su Python 2 (usa invece itertools.izip ), non tanto su Python 3. Questo perché zipin Python 2 creerà un nuovo elenco, ma su Python 3 restituirà solo un generatore (pigro).
kennytm

OK, quindi dovrei attenermi alla tua seconda proposta, perché questa costituisce la parte centrale del mio codice.
fuenfundachtzig

4
@ 85: perché ti preoccupi delle prestazioni? Scrivi quello che devi fare, se è lento, quindi prova per trovare i colli di bottiglia.
Gary Kerr

1
@PreludeAndFugue: se ci sono due opzioni equivalenti, è bene sapere quale è più veloce e usarla subito.
fuenfundachtzig

1
Puoi semplicemente usarlo from itertools import izipe usarlo al posto del zipprimo esempio. Questo crea un iteratore, lo stesso di Python 3.
Chris B.

28

Vedo 2 opzioni.

  1. Utilizzando 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. Utilizzando una comprensione della lista e comprimila:

    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
L'uso di Numpy è un buon suggerimento poiché l'OP sembra voler memorizzare i numeri negli elenchi. Un array bidimensionale sarebbe anche meglio.
Philipp

È anche un buon suggerimento perché questa sarà una sintassi molto familiare agli utenti di R, dove questo tipo di selezione è molto potente, specialmente se annidato e / o multidimensionale.
Thomas Browne

[property_b[i] for i in good_indices]è buono per l'utilizzo senzanumpy
Ilya Rusin

16

Usa la funzione zip incorporata

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

MODIFICARE

Basta guardare le nuove funzionalità di 2.7. Ora c'è una funzione nel modulo itertools che è simile al codice sopra.

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

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

1
Sono deluso dall'uso di itertools.compressqui. La comprensione della lista è molto più leggibile, senza dover scavare cosa sta facendo il compressore.
PaulMcG

5
Hm, trovo il codice che utilizza compress molto più leggibile :) Forse sono di parte, perché fa esattamente quello che voglio.
fuenfundachtzig

8

Supponendo che tu abbia solo l'elenco degli elementi e un elenco di indici veri / obbligatori, questo dovrebbe essere il più veloce:

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

Ciò significa che la selezione della proprietà eseguirà solo tanti round quanti sono gli indici true / required. Se hai molti elenchi di proprietà che seguono le regole di un singolo elenco di tag (vero / falso), puoi creare un elenco di indici utilizzando gli stessi principi di comprensione dell'elenco:

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

Questo itera attraverso ogni elemento in good_objects (ricordando il suo indice con enumerate) e restituisce solo gli indici in cui l'elemento è vero.


Per chiunque non abbia la comprensione della lista, ecco una versione in prosa inglese con il codice evidenziato in grassetto:

elencare l' indice per ogni gruppo di indice, elemento che esiste in un'enumerazione di oggetti buoni , se (dove) la voce è vera


0

I linguaggi Matlab e Scilab offrono una sintassi più semplice ed elegante di Python per la domanda che stai ponendo, quindi penso che il meglio che puoi fare sia imitare Matlab / Scilab usando il pacchetto Numpy in Python. In questo modo la soluzione al tuo problema è molto concisa ed elegante:

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 cerca di imitare Matlab / Scilab ma ha un costo: devi dichiarare ogni lista con la parola chiave "array", cosa che sovraccaricherà il tuo script (questo problema non esiste con Matlab / Scilab). Nota che questa soluzione è limitata agli array di numeri, come nel tuo esempio.


3
In nessun punto della domanda menziona NumPy: non è necessario esprimere la tua opinione su NumPy vs Matlab. Gli elenchi Python non sono la stessa cosa degli array NumPy, anche se entrambi corrispondono approssimativamente a vettori. (Gli elenchi Python sono come gli array di celle Matlab: ogni elemento può avere un diverso tipo di dati. Gli array NumPy sono più limitati per consentire determinate ottimizzazioni). Puoi ottenere una sintassi simile al tuo esempio tramite filterla libreria integrata di Python o la libreria esterna pandas. Se hai intenzione di scambiare le lingue, potresti anche provare R, ma non è quello che si pone la domanda .
Livius
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.