Python: seleccione un subconjunto de la lista según el conjunto de índices


98

Tengo varias listas que tienen el mismo número de entradas (cada una especifica una propiedad de objeto):

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

y lista con banderas de la misma longitud

good_objects = [True, False, False, True]

(que podría sustituirse fácilmente con una lista de índice equivalente:

good_indices = [0, 3]

¿Cuál es la forma más sencilla de generar nuevas listas property_asel, property_bsel, ..., que contienen sólo los valores indicados, ya sea por las Trueentradas o los índices?

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

Respuestas:


126

Podrías usar la comprensión de listas :

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]

El último es más rápido porque hay menos good_indicesde la longitud de property_a, asumiendo que good_indicesse calculan previamente en lugar de generarlos sobre la marcha.


Editar : la primera opción es equivalente a la itertools.compressdisponible desde Python 2.7 / 3.1. Vea la respuesta de @Gary Kerr .

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

1
@fuen: Sí. Causa mucho en Python 2 (use itertools.izip en su lugar), no tanto en Python 3. Esto se debe a que zipen Python 2 creará una nueva lista, pero en Python 3 simplemente devolverá un generador (perezoso).
kennytm

Bien, entonces debería ceñirme a su segunda propuesta, porque esta constituye la parte central de mi código.
fuenfundachtzig

4
@ 85: ¿por qué te preocupas por el rendimiento? Escriba lo que tiene que hacer, si es lento, luego pruebe para encontrar cuellos de botella.
Gary Kerr

1
@PreludeAndFugue: Si hay dos opciones equivalentes, es bueno saber cuál es más rápida y usarla de inmediato.
fuenfundachtzig

1
Puede usarlo from itertools import izipy usarlo en lugar de zipen el primer ejemplo. Eso crea un iterador, igual que Python 3.
Chris B.

28

Veo 2 opciones.

  1. Usando 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. Usando una lista de comprensión y comprímala:

    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
Usar Numpy es una buena sugerencia ya que el OP parece querer almacenar números en listas. Una matriz bidimensional sería incluso mejor.
Philipp

También es una buena sugerencia porque será una sintaxis muy familiar para los usuarios de R, donde este tipo de selección es muy poderosa, especialmente cuando está anidada y / o multidimensional.
Thomas Browne

[property_b[i] for i in good_indices]es bueno para usar sinnumpy
Ilya Rusin

16

Utilice la función zip incorporada

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

EDITAR

Solo mirando las nuevas características de 2.7. Ahora hay una función en el módulo itertools que es similar al código anterior.

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

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

1
Estoy decepcionado por el uso de itertools.compresshere. La comprensión de la lista es mucho más legible, sin tener que desenterrar lo que está haciendo la compresión.
PaulMcG

5
Hm, encuentro que el código usando compress es mucho más legible :) Quizás estoy sesgado, porque hace exactamente lo que quiero.
fuenfundachtzig

8

Suponiendo que solo tiene la lista de elementos y una lista de índices verdaderos / requeridos, este debería ser el más rápido:

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

Esto significa que la selección de propiedades solo hará tantas rondas como índices verdaderos / requeridos. Si tiene muchas listas de propiedades que siguen las reglas de una sola lista de etiquetas (verdadero / falso), puede crear una lista de índices utilizando los mismos principios de comprensión de listas:

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

Esto itera a través de cada elemento en good_objects (mientras recuerda su índice con enumerate) y devuelve solo los índices donde el elemento es verdadero.


Para cualquiera que no obtenga la comprensión de la lista, aquí hay una versión en prosa en inglés con el código resaltado en negrita:

enumere el índice para cada grupo de índice, elemento que existe en una enumeración de buenos objetos , si (donde) el elemento es Verdadero


0

Los lenguajes Matlab y Scilab ofrecen una sintaxis más simple y elegante que Python para la pregunta que está haciendo, así que creo que lo mejor que puede hacer es imitar Matlab / Scilab usando el paquete Numpy en Python. Al hacer esto, la solución a su problema es muy concisa y 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 intenta imitar a Matlab / Scilab, pero tiene un costo: debes declarar cada lista con la palabra clave "matriz", algo que sobrecargará tu script (este problema no existe con Matlab / Scilab). Tenga en cuenta que esta solución está restringida a matrices de números, que es el caso de su ejemplo.


3
En ninguna parte de la pregunta menciona a NumPy; no es necesario que expreses tu opinión sobre NumPy vs Matlab. Las listas de Python no son lo mismo que las matrices NumPy, incluso si ambas corresponden aproximadamente a vectores. (Las listas de Python son como las matrices de celdas de Matlab: cada elemento puede tener un tipo de datos diferente. Las matrices de NumPy están más restringidas para permitir ciertas optimizaciones). Puede obtener una sintaxis similar a su ejemplo a través de filterla biblioteca integrada o externa de Python pandas. Si va a intercambiar idiomas, también puede probar R, pero eso no es lo que pregunta la pregunta .
Livius
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.