Acceda a múltiples elementos de la lista conociendo su índice

232

Necesito elegir algunos elementos de la lista dada, conociendo su índice. Digamos que me gustaría crear una nueva lista, que contiene elementos con índice 1, 2, 5, de la lista dada [-2, 1, 5, 3, 8, 5, 6]. Lo que hice es:

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

¿Hay alguna forma mejor de hacerlo? algo como c = a [b]?

hoang tran
fuente
1
Por cierto, encontré otra solución aquí. Todavía no lo he probado, pero creo que puedo publicarlo aquí una vez que esté interesado en code.activestate.com/recipes/…
hoang tran
Esa es la misma solución mencionada en la pregunta, pero envuelta en una lambdafunción.
Will Dereham

Respuestas:

218

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

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

Pero realmente, su solución actual está bien. Es probablemente el más lindo de todos ellos.

TerryA
fuente
35
+1 por mencionar que c = [a[i] for i in b]está perfectamente bien. Tenga en cuenta que la itemgettersolución no hará lo mismo si b tiene menos de 2 elementos.
flornquake
Lado Nota : El uso de itemgetter mientras se trabaja en múltiples proceso no funciona. Numpy funciona muy bien en multiprocesos.
Lior Magen
3
Comentario adicional, soloa[b] funciona cuando hay una matriz numpy , es decir, la crea con una función numpy. a
Ludwig Zhou
He comparado las opciones que no son numpy y el itemgetter parece ser el más rápido, incluso un poco más rápido que simplemente escribir los índices deseados entre paréntesis, usando Python 3.44
ragardner
@ citizen2077, ¿puede dar un ejemplo de la sintaxis que describe?
alancalvitti
47

Alternativas:

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

>>> import operator
>>> operator.itemgetter(*b)(a)
(1, 5, 5)
falsetru
fuente
el primero es bueno porque usas build-infunciones
silgon
El problema con el primero es que __getitem__no parece ser comparable, por ejemplo, ¿cómo mapear el tipo de elemento? map(type(a.__getitem__), b)
alancalvitti
@alancalvitti, lambda x: type(a.__getitem__(x)), b. En este caso, el uso [..]es más compacto:lambda x: type(a[x]), b
falsetru
9

Otra solución podría ser a través de la serie pandas:

import pandas as pd

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

Luego puede convertir c nuevamente a una lista si desea:

c = list(c)
Bossa Nova
fuente
7

Pruebas básicas y no muy extensas que comparan el tiempo de ejecución de las cinco respuestas proporcionadas:

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]

utilizando la siguiente entrada:

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

el bucle simple de Python fue el más rápido con la operación lambda en segundo lugar, mapIndexValues ​​y getIndexValues ​​fueron bastante similares con el método numpy significativamente más lento después de convertir las listas en matrices numpy. Si los datos ya están en matrices numpy, el método numpyIndexValues ​​con la conversión numpy.array eliminada es más 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
Don Smythe
fuente
No sé qué intérprete de Python utiliza, pero el primer método numpyIndexValuesno funciona a, ya que bson de tipo range. ¿Supongo que intentas convertir a, ba numpy.ndarraysprimero?
strpeter
@strpeter Sí, no estaba comparando manzanas con manzanas, había creado matrices numpy como entrada en el caso de prueba para numpyIndexValues. He solucionado esto ahora y todos usan las mismas listas como entrada.
Don Smythe
4

Estoy seguro de que esto ya se ha considerado: si la cantidad de índices en b es pequeña y constante, uno podría escribir el resultado como:

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

O incluso más simple si los índices en sí son constantes ...

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

O si hay un rango consecutivo de índices ...

c = a[1:3] + [a[5]]
ecp
fuente
Gracias por recordarme eso[a] + [b] = [a, b]
onewhaleid
3

Aquí hay una manera más simple:

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

Mi respuesta no usa colecciones numpy o python.

Una forma trivial de encontrar elementos sería la siguiente:

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

Inconveniente: este método puede no funcionar para listas más grandes. Se recomienda usar numpy para listas más grandes.

Lavya 'Orion'
fuente
55
No es necesario iterar a. [a[i] for i in b]
falsetru
1
Este método ni siquiera funciona en ningún otro caso. ¿Qué pasaría si atuviera otros 5?
TerryA
OMI, más rápido para hacer este tipo de intersección utilizando conjuntos
sirgogo
Si está preocupado por IndexErrors si b tiene números que exceden el tamaño de a, intente[a[i] if i<len(a) else None for i in b]
576i
0

Índices estáticos y pequeña lista?

No olvide que si la lista es pequeña y los índices no cambian, como en su ejemplo, a veces lo mejor es utilizar el desempaquetado de secuencias :

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

El rendimiento es mucho mejor y también puede guardar una línea 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
GM
fuente
0

Tipo de forma pitónica:

c = [x for x in a if a.index(x) in b]
Dmitry K. Chebanov
fuente
2
Yo diría que esto es menos "pitónico" que incluso el ejemplo del OP: ha logrado convertir su O(n)solución en una O(n^2)solución y casi duplicar la longitud del código. También querrá tener en cuenta que el enfoque fallará si la lista contiene objetos difusos o de igualdad parcial, por ejemplo, si acontiene float('nan'), esto siempre elevará a ValueError.
Brian