web-dev-qa-db-fra.com

Python: des moyens élégants et efficaces pour masquer une liste

Exemple:

from __future__ import division
import numpy as np

n = 8
"""masking lists"""
lst = range(n)
print lst

# the mask (filter)
msk = [(el>3) and (el<=6) for el in lst]
print msk

# use of the mask
print [lst[i] for i in xrange(len(lst)) if msk[i]]

"""masking arrays"""
ary = np.arange(n)
print ary

# the mask (filter)
msk = (ary>3)&(ary<=6)
print msk

# use of the mask
print ary[msk]                          # very elegant  

et les résultats sont:

>>> 
[0, 1, 2, 3, 4, 5, 6, 7]
[False, False, False, False, True, True, True, False]
[4, 5, 6]
[0 1 2 3 4 5 6 7]
[False False False False  True  True  True False]
[4 5 6]

Comme vous le voyez, l'opération de masquage sur le tableau est plus élégante que la liste. Si vous essayez d'utiliser le schéma de masquage de tableau dans la liste, vous obtiendrez une erreur:

>>> lst[msk]
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
TypeError: only integer arrays with one element can be converted to an index

La question est de trouver un masquage élégant pour lists.

Mises à jour:
La réponse de jamylak a été acceptée pour introduire compress mais les points mentionnés par Joel Cornett a rendu la solution complète à la forme souhaitée de mon intérêt.

>>> mlist = MaskableList
>>> mlist(lst)[msk]
>>> [4, 5, 6]
19
Developer

Tu recherches itertools.compress

Exemple tiré de la documentation

Équivalent à:

def compress(data, selectors):
    # compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F
    return (d for d, s in izip(data, selectors) if s)
26
jamylak

Étant donné que jamylak a déjà répondu à la question par une réponse pratique, voici mon exemple de liste avec prise en charge du masquage intégrée (totalement inutile, au fait):

from itertools import compress
class MaskableList(list):
    def __getitem__(self, index):
        try: return super(MaskableList, self).__getitem__(index)
        except TypeError: return MaskableList(compress(self, index))

Usage:

>>> myList = MaskableList(range(10))
>>> myList
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> mask = [0, 1, 1, 0]
>>> myList[mask]
[1, 2]

Notez que compress s'arrête lorsque les données ou le masque s'épuisent. Si vous souhaitez conserver la partie de la liste qui s'étend au-delà de la longueur du masque, vous pouvez essayer quelque chose comme:

from itertools import izip_longest

[i[0] for i in izip_longest(myList, mask[:len(myList)], fillvalue=True) if i[1]]
7
Joel Cornett

Si vous utilisez Numpy, vous pouvez le faire facilement en utilisant le tableau Numpy sans installer aucune autre bibliothèque:

>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>> msk = [ True, False, False,  True,  True,  True,  True, False, False, False]
>> a = np.array(a) # convert list to numpy array
>> result = a[msk] # mask a
>> result.tolist()
[0, 3, 4, 5, 6]
5
biendltb

je ne le trouve pas élégant. Il est compact, mais a tendance à prêter à confusion, car la construction est très différente de la plupart des langues.

Comme Rossum l'a dit à propos de la conception du langage, nous passons plus de temps à le lire qu'à l'écrire. Plus la construction d'une ligne de code est obscure, plus elle devient confuse pour les autres, qui peuvent manquer de familiarité avec Python, même s'ils ont une compétence complète dans un certain nombre d'autres langues.

La lisibilité l'emporte sur les notations courtes tous les jours dans le monde réel du code de maintenance. Tout comme réparer votre voiture. De gros dessins avec beaucoup d'informations facilitent le dépannage.

Pour moi, je préfère de loin dépanner le code de quelqu'un qui utilise le formulaire long

print [lst[i] for i in xrange(len(lst)) if msk[i]]

que le masque de notation courte numpy. Je n'ai pas besoin d'avoir une connaissance particulière d'un package Python spécifique pour l'interpréter).

3
Jim