Filter elements from list based on True/False from another list
Asked Answered
P

5

5

Is there an idiomatic way to mask elements of an array in vanilla Python 3? For example:

a = [True, False, True, False]
b = [2, 3, 5, 7]
b[a]

I was hoping b[a] would return [2, 5], but I get an error:

TypeError: list indices must be integers or slices, not list

In R, this works as I expected (using c() instead of [] to create the lists). I know NumPy has MaskedArray that can do this, I'm looking for an idiomatic way to do this in plain vanilla Python. Of course, I could use a loop and iterate through the mask list and the element list, but I'm hoping there's a more efficient way to mask elements using a higher level abstraction.

Piercy answered 3/1, 2021 at 9:43 Comment(6)
There's no syntax for masked indexing. As for builtins, probably most would just use a list comp with a filter clause or maybe docs.python.org/3/library/itertools.html#itertools.compress. As an aside, in numpy, you don't need masked arrays for boolean indexing---it works on any array.Manolete
@alkasm, that was fast! Exactly what I was looking for! If you create this response as an answer I'll award you the credit.Piercy
I appreciate the sentiment, but no use in re-answering---award to a current answer!Manolete
Wrapping in a Series, the syntax is about what you thought : Series(b)[a], I've answer with it belowFourinhand
@Fourinhand I was looking for something in vanilla PythonPiercy
@Piercy You can also create a class for this like in my answerCopier
T
6

You can use itertools.compress:

>>> from itertools import compress
>>> a = [True, False, True, False]
>>> b = [2, 3, 5, 7]


>>> list(compress(b, a))
[2, 5]

Refer "itertools.compress()" document for more details

Trichina answered 3/1, 2021 at 9:47 Comment(0)
W
3

You could use a list-comprehension:

[b[i] for i in range(len(a)) if a[i]]

or

[b[i] for i,mask in enumerate(a) if maks]

In both cases a list is created by iterating over each element and only inserting it if the mask is true.

Wynny answered 3/1, 2021 at 9:47 Comment(2)
Please don't post only code as answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes.Enclosure
Thought it was short-enough to be self-explanatory. I added a short explanation. Thank you for making me aware.Wynny
C
2

I think there aren't a lot of ways to do this, you could use a zip:

print([y for x, y in zip(a, b) if x])

Output:

[2, 5]

You could also create a class with __getitem__ for this:

class index:
    def __init__(self, seq):
        self.seq = seq
    def __getitem__(self, boolseq):
        return [x for x, y in zip(boolseq, self.seq) if y]
print(index(a)[b])

Output:

[2, 5]
Copier answered 3/1, 2021 at 9:47 Comment(0)
F
2

You can use a pandas.Series which allows, like for dataframe, to filter data with a boolean array

from pandas import Series

a = [True, False, True, False]
b = [2, 3, 5, 7]

res = Series(b)[a].tolist()

print(res)  # [2, 5]
Fourinhand answered 3/1, 2021 at 9:53 Comment(0)
D
1

Among the many options you can:

1. Use itertools compress

from itertools import compress
a = [True, False, True, False]
b = [2, 3, 5, 7]

result_itertools = list(compress(b, a))
print(result_itertools)

2. Use Filter Function

result_filter = list(filter(lambda x: x[0], zip(a, b)))
for item in result_filter:
    print(item[1])
# 2
# 5

3. Use List Comprehension

result_comprehension = [value for bool_, value in zip(a, b) if bool_]
print(result_comprehension)
# [2, 5]
Drone answered 3/1, 2021 at 9:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.