Python: элегантные и эффективные способы маскировки списка

пример:

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  

и результаты:

>>> 
[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]

как вы видите, операция маскировки на массиве более элегантна по сравнению со списком. Если вы попытаетесь использовать схему маскировки массива в списке, вы получите сообщение об ошибке:

>>> 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

вопрос в том, чтобы найти элегантное маски для lists.

обновления:
Ответ jamylak было принято для введения очки упоминается Joel Cornett сделал решение полным до желаемой формы моего интереса.

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

4 ответов


вы ищете itertools.compress

пример из docs

эквивалентно:

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)

поскольку джамылак уже ответил на вопрос практическим ответом, вот мой пример списка со встроенной поддержкой маскировки (совершенно ненужной, кстати):

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))

использование:

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

отметим, что compress останавливается, когда заканчиваются данные или маска. Если вы хотите сохранить часть списка, которая выходит за пределы длины маски, вы можете попробовать что-то вроде:

from itertools import izip_longest

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

Я не считаю его элегантным. Он компактен, но имеет тенденцию сбивать с толку, поскольку конструкция сильно отличается от большинства языков.

читаемость козырей короткая форма обозначения каждый день в реальном мире обслуживания кода. Так же, как починить машину. Большие чертежи с большим количеством информации делают устранение неполадок намного проще.

для меня я бы предпочел устранить неполадки чьего-то кода, который использует длинную форму

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

чем маска короткой нотации numpy. Мне не нужно иметь никаких специальных знаний о конкретном пакете Python для его интерпретации.


Если вы используете Numpy, вы можете сделать это легко, используя массив Numpy без установки любой другой библиотеки:

>> 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]