Obtendo índices de valores verdadeiros em uma lista booleana

87

Eu tenho um trecho do meu código onde devo criar um painel de controle. Quero retornar uma lista de todos os interruptores que estão ativados. Aqui, "on" será igual Truee "off" será igual False. Agora, quero apenas retornar uma lista de todos os Truevalores e suas posições. Isso é tudo que tenho, mas ele apenas retorna a posição da primeira ocorrência de True(esta é apenas uma parte do meu código):

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]

Isso retorna apenas "4"

Charles Smith
fonte

Respostas:

115

Use enumerate, list.indexretorna o índice da primeira correspondência encontrada.

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

Para listas grandes, é melhor usar 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
Ashwini Chaudhary
fonte
Ahh entendo, vi algumas perguntas semelhantes me dizendo para usar enumerar, mas acho que estava usando errado. Eu estava definindo a lista igual a x, então fazendo, enumerate(x)mas acho que tudo que eu estava fazendo era enumerar 4? É isso que estava acontecendo? Obrigado pela ajuda
Charles Smith
Além disso, o que está acontecendo quando você faz i for i, xa compreensão da lista? Só estou acostumado a ver i for ipor exemplo, ou um formato semelhante, qual é a função de x? Obrigado
Charles Smith
1
@Amon enumerateretorna um tuplas (ind, valor) durante o loop, agora podemos atribuir os itens da tupla a duas variáveis usando: i, x = (ind, value). Isso é exatamente o que está acontecendo nesse loop.
Ashwini Chaudhary
Oh, eu vejo o que está acontecendo agora. Muito obrigado pela sua ajuda!
Charles Smith
Para qualquer pessoa que use Python3, na itertools.compresssolução, altere o xrangepararange . ( xrangefoi renomeado para rangeem Python 3.)
MehmedB
64

Se você tiver numpy disponível:

>>> 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])
Jterrace
fonte
8
Observe que isso retorna uma tupla que requer np.where(states)[0]o uso real dos resultados
Rufus
17

TL; DR : use np.where, pois é a opção mais rápida. Suas opções são np.where, itertools.compresselist comprehension .

Veja a comparação detalhada abaixo, onde pode ser visto que np.wheresupera ambos itertools.compresse tambémlist 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
  • Método 1: usando 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)
  • Método 2: usando itertools.compress
>>> %timeit list(compress(range(len(t)), t))
210 µs ± 704 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
  • Método 3 (o método mais rápido): usando numpy.where
>>> %timeit np.where(t)
179 µs ± 593 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Meysam Sadeghi
fonte
2

Você pode usar o filtro para isso:

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

O rangehere enumera os elementos da sua lista e como queremos apenas aqueles onde self.statesestá True, estamos aplicando um filtro com base nesta condição.

Para Python> 3.0:

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

Sashkello
fonte
1

Use a forma de compreensão do dicionário,

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

Entrada:

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

Resultado:

{4: True, 5: True, 7: True}
Principiante
fonte
3
É uma compreensão de dicionário, não compreensão de lista.
Ashwini Chaudhary
1

Usando a multiplicação por elemento e um conjunto:

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

Resultado: {4, 5, 7}

Nate
fonte
1

Basta fazer isso:

def which_index(self):
    return [
        i for i in range(len(self.states))
        if self.states[i] == True
    ]
ArnabJyoti Thakuria
fonte
Obrigado por sua contribuição e bem-vindo ao StackOverflow. No entanto, leia a Ajuda de edição para melhorar sua formatação e também adicione alguma explicação ao seu código. Obrigado!
Será em