Getting indices of True values in a boolean list

Question:

I have a piece of my code where I’m supposed to create a switchboard. I want to return a list of all the switches that are on. Here “on” will equal True and “off” equal False. So now I just want to return a list of all the True values and their position. This is all I have but it only return the position of the first occurrence of True (this is just a portion of my code):

self.states = [False, False, False, False, True, True, False, True, False, False, False, False, False, False, False, False]

def which_switch(self):
    x = [self.states.index(i) for i in self.states if i == True]

This only returns “4”

Asked By: Amon

||

Answers:

Use enumerate, list.index returns the index of first match found.

>>> t = [False, False, False, False, True, True, False, True, False, False, False, False, False, False, False, False]
>>> [i for i, x in enumerate(t) if x]
[4, 5, 7]

For huge lists, it’d be better to use itertools.compress:

>>> from itertools import compress
>>> list(compress(xrange(len(t)), t))
[4, 5, 7]
>>> t = t*1000
>>> %timeit [i for i, x in enumerate(t) if x]
100 loops, best of 3: 2.55 ms per loop
>>> %timeit list(compress(xrange(len(t)), t))
1000 loops, best of 3: 696 µs per loop
Answered By: Ashwini Chaudhary

You can use filter for it:

filter(lambda x: self.states[x], range(len(self.states)))

The range here enumerates elements of your list and since we want only those where self.states is True, we are applying a filter based on this condition.

For Python > 3.0:

list(filter(lambda x: self.states[x], range(len(self.states))))

Answered By: sashkello

Use dictionary comprehension way,

x = {k:v for k,v in enumerate(states) if v == True}

Input:

states = [False, False, False, False, True, True, False, True, False, False, False, False, False, False, False, False]

Output:

{4: True, 5: True, 7: True}
Answered By: Beginner

If you have numpy available:

>>> import numpy as np
>>> states = [False, False, False, False, True, True, False, True, False, False, False, False, False, False, False, False]
>>> np.where(states)[0]
array([4, 5, 7])
Answered By: jterrace

Using element-wise multiplication and a set:

>>> states = [False, False, False, False, True, True, False, True, False, False, False, False, False, False, False, False]
>>> set(multiply(states,range(1,len(states)+1))-1).difference({-1})

Output:
{4, 5, 7}

Answered By: Nate

Simply do this:

def which_index(self):
    return [
        i for i in range(len(self.states))
        if self.states[i] == True
    ]
Answered By: ArnabJyoti Thakuria

TL; DR: use np.where as it is the fastest option. Your options are np.where, itertools.compress, and list comprehension.

See the detailed comparison below, where it can be seen np.where outperforms both itertools.compress and also list comprehension.

>>> from itertools import compress
>>> import numpy as np
>>> t = [False, False, False, False, True, True, False, True, False, False, False, False, False, False, False, False]`
>>> t = 1000*t
  • Method 1: Using list comprehension
>>> %timeit [i for i, x in enumerate(t) if x]
457 µs ± 1.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
  • Method 2: Using itertools.compress
>>> %timeit list(compress(range(len(t)), t))
210 µs ± 704 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
  • Method 3 (the fastest method): Using numpy.where
>>> %timeit np.where(t)
179 µs ± 593 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Answered By: Meysam Sadeghi

I got different benchmark result compared to @meysham answer. In this test, compress seems the fastest (python 3.7).

from itertools import compress
import numpy as np
t = [True, False, False, False, False, True, True, False, True, False, False, False, False, False, False, False, False]

%timeit [i for i, x in enumerate(t) if x]
%timeit list(compress(range(len(t)), t))
%timeit list(filter(lambda x: t[x], range(len(t))))
%timeit np.where(t)[0]

# 2.54 µs ± 400 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
# 2.67 µs ± 600 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
# 6.22 µs ± 624 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
# 6.52 µs ± 768 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
t = 1000*t
%timeit [i for i, x in enumerate(t) if x]
%timeit list(compress(range(len(t)), t))
%timeit list(filter(lambda x: t[x], range(len(t))))
%timeit np.where(t)[0]

# 1.68 ms ± 112 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# 947 µs ± 105 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# 3.96 ms ± 97 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# 2.14 ms ± 45.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Answered By: Muhammad Yasirroni

You can filter by using boolean mask array with square bracket, it’s faster than np.where

>>> states = [True, False, False, True]
>>> np.arange(len(states))[states]
array([0, 3])
>>> size = 1_000_000
>>> states = np.arange(size) % 2 == 0
>>> states 
array([ True, False,  True, ..., False,  True, False])
>>> true_index = np.arange(size)[states]
>>> len(true_index)
500000
>>> true_index
array([     0,      2,      4, ..., 999994, 999996, 999998])
Answered By: KevinBui
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.