Acesse vários elementos da lista conhecendo seu índice


232

Preciso escolher alguns elementos da lista fornecida, conhecendo seu índice. Digamos que eu gostaria de criar uma nova lista, que contenha elemento com o índice 1, 2, 5, da lista fornecida [-2, 1, 5, 3, 8, 5, 6]. O que eu fiz é:

a = [-2,1,5,3,8,5,6]
b = [1,2,5]
c = [ a[i] for i in b]

Existe alguma maneira melhor de fazer isso? algo como c = a [b]?


1
a propósito, eu encontrei outra solução aqui. Eu não testá-lo ainda, mas eu acho que eu posso postar aqui uma vez que você está interessado em code.activestate.com/recipes/...
Hoang tran

Essa é a mesma solução mencionada na pergunta, mas envolvida em uma lambdafunção.
que você precisa

Respostas:


218

Você pode usar operator.itemgetter:

from operator import itemgetter 
a = [-2, 1, 5, 3, 8, 5, 6]
b = [1, 2, 5]
print(itemgetter(*b)(a))
# Result:
(1, 5, 5)

Ou você pode usar numpy :

import numpy as np
a = np.array([-2, 1, 5, 3, 8, 5, 6])
b = [1, 2, 5]
print(list(a[b]))
# Result:
[1, 5, 5]

Mas, na verdade, sua solução atual está correta. É provavelmente o mais legal de todos eles.


35
+1 por mencionar que c = [a[i] for i in b]está perfeitamente bem. Observe que a itemgettersolução não fará a mesma coisa se b tiver menos de 2 elementos.
Flornquake

Nota lateral : Usar o itemgetter enquanto trabalha em multiprocessos não funciona. O Numpy funciona muito bem em multiprocessos.
Lior Magen 29/03

3
Comentário adicional, a[b]funciona apenas quando aé uma matriz numpy , ou seja, você a cria com uma função numpy.
Ludwig Zhou

Tenho comparou as opções não numpy e itemgetter parece ser o mais rápido, mesmo ligeiramente mais rápido do que simplesmente digitando os índices desejados entre parênteses, utilizando Python 3,44
ragardner

@ citizen2077, você pode dar um exemplo da sintaxe que descreve?
precisa saber é o seguinte

47

Alternativas:

>>> map(a.__getitem__, b)
[1, 5, 5]

>>> import operator
>>> operator.itemgetter(*b)(a)
(1, 5, 5)

o primeiro é bom porque você usar build-infunções
silgon

O problema com o primeiro é que __getitem__não parece ser comparável, por exemplo, como mapear o tipo do item? map(type(a.__getitem__), b)
precisa saber é o seguinte

@alancalvitti lambda x: type(a.__getitem__(x)), b,. Nesse caso, o uso [..]é mais compacto:lambda x: type(a[x]), b
falsetru

9

Outra solução poderia ser via pandas Series:

import pandas as pd

a = pd.Series([-2, 1, 5, 3, 8, 5, 6])
b = [1, 2, 5]
c = a[b]

Em seguida, você pode converter c em uma lista, se desejar:

c = list(c)

7

Teste básico e não muito extenso, comparando o tempo de execução das cinco respostas fornecidas:

def numpyIndexValues(a, b):
    na = np.array(a)
    nb = np.array(b)
    out = list(na[nb])
    return out

def mapIndexValues(a, b):
    out = map(a.__getitem__, b)
    return list(out)

def getIndexValues(a, b):
    out = operator.itemgetter(*b)(a)
    return out

def pythonLoopOverlap(a, b):
    c = [ a[i] for i in b]
    return c

multipleListItemValues = lambda searchList, ind: [searchList[i] for i in ind]

usando a seguinte entrada:

a = range(0, 10000000)
b = range(500, 500000)

loop python simples foi o mais rápido com a operação lambda por um segundo próximo, mapIndexValues ​​e getIndexValues ​​eram consistentemente bastante semelhantes ao método numpy significativamente mais lento após a conversão de listas em matrizes numpy.Se os dados já estiverem em matrizes numpy, o método numpyIndexValues ​​com a conversão numpy.array removida é mais rápido.

numpyIndexValues -> time:1.38940598 (when converted the lists to numpy arrays)
numpyIndexValues -> time:0.0193445 (using numpy array instead of python list as input, and conversion code removed)
mapIndexValues -> time:0.06477512099999999
getIndexValues -> time:0.06391049500000001
multipleListItemValues -> time:0.043773591
pythonLoopOverlap -> time:0.043021754999999995

Não sei qual interpretador Python você usa, mas o primeiro método numpyIndexValuesnão funciona desde então a, bé do tipo range. Eu estou supondo que você mento para converter a, ba numpy.ndarraysprimeira?
Strpeter 14/10

@ strpeter Sim, eu não estava comparando maçãs com maçãs, havia criado matrizes numpy como entrada no caso de teste para os valores numpyIndexValues. Corrigi isso agora e todos usam as mesmas listas de entrada.
Don Smythe

4

Tenho certeza de que isso já foi considerado: se a quantidade de índices em b for pequena e constante, basta escrever o resultado da seguinte forma:

c = [a[b[0]]] + [a[b[1]]] + [a[b[2]]]

Ou ainda mais simples se os próprios índices forem constantes ...

c = [a[1]] + [a[2]] + [a[5]]

Ou se houver um intervalo consecutivo de índices ...

c = a[1:3] + [a[5]]

Obrigado por me lembrar que[a] + [b] = [a, b]
onewhaleid 3/17/17

3

Aqui está uma maneira mais simples:

a = [-2,1,5,3,8,5,6]
b = [1,2,5]
c = [e for i, e in enumerate(a) if i in b]

1

Minha resposta não usa coleções numpy ou python.

Uma maneira trivial de encontrar elementos seria a seguinte:

a = [-2, 1, 5, 3, 8, 5, 6]
b = [1, 2, 5]
c = [i for i in a if i in b]

Desvantagem: esse método pode não funcionar para listas maiores. O uso de numpy é recomendado para listas maiores.


5
Não há necessidade de iterar a. [a[i] for i in b]
falsetru 22/09/14

1
Esse método nem funciona em nenhum outro caso. E se ativesse outros 5 nele?
precisa saber é o seguinte

IMO, mais rápido para fazer esse tipo de interseção usando conjuntos
sirgogo 15/03

Se você está preocupado com IndexErrors se b tem números que ultrapassam o tamanho é, tente[a[i] if i<len(a) else None for i in b]
576i

0

Índices estáticos e pequena lista?

Não esqueça que se a lista for pequena e os índices não mudarem, como no seu exemplo, às vezes o melhor é usar a descompactação de sequência :

_,a1,a2,_,_,a3,_ = a

O desempenho é muito melhor e você também pode salvar uma linha de código:

 %timeit _,a1,b1,_,_,c1,_ = a
10000000 loops, best of 3: 154 ns per loop 
%timeit itemgetter(*b)(a)
1000000 loops, best of 3: 753 ns per loop
 %timeit [ a[i] for i in b]
1000000 loops, best of 3: 777 ns per loop
 %timeit map(a.__getitem__, b)
1000000 loops, best of 3: 1.42 µs per loop

0

Tipo de maneira pitônica:

c = [x for x in a if a.index(x) in b]

2
Eu diria que isso é menos "pitônico" do que o exemplo do OP - você conseguiu transformar a O(n)solução em uma O(n^2)solução e, ao mesmo tempo, quase dobrou o tamanho do código. Você também deve observar que a abordagem falhará se a lista contiver objetos com igualdade difusa ou parcial; por exemplo, se acontiver float('nan'), isso sempre aumentará a ValueError.
Brian
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.