Python: sélectionnez un sous-ensemble dans la liste en fonction de l'ensemble d'index

98

J'ai plusieurs listes ayant toutes le même nombre d'entrées (chacune spécifiant une propriété d'objet):

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

et liste avec des drapeaux de même longueur

good_objects = [True, False, False, True]

(qui pourrait facilement être remplacée par une liste d'index équivalente:

good_indices = [0, 3]

Quelle est la meilleure façon de générer de nouvelles listes property_asel, property_bsel... qui ne contiennent que les valeurs indiquées soit par les Trueentrées ou les indices?

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

Réponses:

126

Vous pouvez simplement utiliser la compréhension de liste :

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

ou

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

Ce dernier est plus rapide car il y en a moins good_indicesque la longueur de property_a, en supposant qu'ils good_indicessont précalculés au lieu d'être générés à la volée.


Edit : La première option est équivalente à itertools.compressdisponible depuis Python 2.7 / 3.1. Voir la réponse de @Gary Kerr .

property_asel = list(itertools.compress(property_a, good_objects))
KennyTM
la source
1
@fuen: Oui. Cause beaucoup sur Python 2 (utilisez itertools.izip à la place), pas tellement sur Python 3. C'est parce que zipdans Python 2 va créer une nouvelle liste, mais sur Python 3, il renverra simplement un générateur (paresseux).
kennytm
OK, je devrais donc m'en tenir à votre deuxième proposition, car cela constitue la partie centrale de mon code.
fuenfundachtzig
4
@ 85: pourquoi vous inquiétez-vous des performances? Écrivez ce que vous devez faire, si cela est lent, puis testez pour trouver les goulots d'étranglement.
Gary Kerr
1
@PreludeAndFugue: S'il y a deux options équivalentes, il est bon de savoir laquelle est la plus rapide et de l'utiliser tout de suite.
fuenfundachtzig
1
Vous pouvez simplement utiliser from itertools import izipet utiliser cela au lieu de zipdans le premier exemple. Cela crée un itérateur, identique à Python 3.
Chris B.
28

Je vois 2 options.

  1. Utilisation de 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. En utilisant une compréhension de liste et en la compressant:

    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
la source
2
L'utilisation de Numpy est une bonne suggestion car l'OP semble vouloir stocker des nombres dans des listes. Un tableau bidimensionnel serait encore mieux.
Philipp
C'est aussi une bonne suggestion car ce sera une syntaxe très familière aux utilisateurs de R, où ce type de sélection est très puissant, surtout lorsqu'il est imbriqué et / ou multidimensionnel.
Thomas Browne
[property_b[i] for i in good_indices]est un bon pour une utilisation sansnumpy
Ilya Rusin
16

Utilisez la fonction zip intégrée

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

ÉDITER

Il suffit de regarder les nouvelles fonctionnalités de 2.7. Il y a maintenant une fonction dans le module itertools qui est similaire au code ci-dessus.

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

itertools.compress('ABCDEF', [1,0,1,0,1,1]) =>
  A, C, E, F
Gary Kerr
la source
1
Je suis déçu par l'utilisation d' itertools.compressici. La compréhension de la liste est beaucoup plus lisible, sans avoir à déterrer ce que fait la compresse.
PaulMcG
5
Hm, je trouve le code utilisant compress beaucoup plus lisible :) Peut-être que je suis biaisé, car il fait exactement ce que je veux.
fuenfundachtzig
8

En supposant que vous ne disposiez que de la liste des éléments et d'une liste des indices vrais / requis, cela devrait être le plus rapide:

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

Cela signifie que la sélection de propriété ne fera que le nombre de tours qu'il y a d'indices vrais / requis. Si vous avez beaucoup de listes de propriétés qui suivent les règles d'une seule liste de balises (vrai / faux), vous pouvez créer une liste d'index en utilisant les mêmes principes de compréhension de liste:

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

Cela parcourt chaque élément de good_objects (tout en se souvenant de son index avec enumerate) et ne retourne que les indices où l'élément est vrai.


Pour ceux qui ne comprennent pas la liste, voici une version en prose en anglais avec le code surligné en gras:

lister l' index pour chaque groupe d' index, élément qui existe dans une énumération de bons objets , si (où) l' élément est Vrai

Eyrofire
la source
0

Les langages Matlab et Scilab offrent une syntaxe plus simple et plus élégante que Python pour la question que vous posez, donc je pense que le mieux que vous puissiez faire est d'imiter Matlab / Scilab en utilisant le package Numpy en Python. En faisant cela, la solution à votre problème est très concise et élégante:

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 essaie d'imiter Matlab / Scilab mais cela a un coût: vous devez déclarer chaque liste avec le mot-clé "array", quelque chose qui surchargera votre script (ce problème n'existe pas avec Matlab / Scilab). Notez que cette solution est limitée aux tableaux de nombres, ce qui est le cas dans votre exemple.

FredAndre
la source
3
Nulle part dans la question il ne mentionne NumPy - il n'est pas nécessaire d'exprimer votre opinion sur NumPy vs Matlab. Les listes Python ne sont pas la même chose que les tableaux NumPy, même si les deux correspondent à peu près à des vecteurs. (Les listes Python sont comme des tableaux de cellules Matlab - chaque élément peut avoir un type de données différent. Les tableaux NumPy sont plus restreints afin de permettre certaines optimisations). Vous pouvez obtenir une syntaxe similaire à votre exemple via filterla bibliothèque intégrée de Python ou la bibliothèque externe pandas. Si vous souhaitez changer de langue, vous pouvez également essayer R, mais ce n'est pas ce que la question pose .
Livius