Accedi a più elementi dell'elenco conoscendo il loro indice


232

Devo scegliere alcuni elementi dall'elenco indicato, conoscendo il loro indice. Supponiamo che vorrei creare un nuovo elenco, che contiene elementi con indice 1, 2, 5, da un determinato elenco [-2, 1, 5, 3, 8, 5, 6]. Quello che ho fatto è:

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

C'è un modo migliore per farlo? qualcosa come c = a [b]?


1
a proposito, ho trovato un'altra soluzione qui. Non l'ho ancora testato, ma penso di poterlo pubblicare qui quando sei interessato a code.activestate.com/recipes/…
hoang tran

Questa è la stessa soluzione menzionata nella domanda, ma racchiusa in una lambdafunzione.
Will Dereham,

Risposte:


218

Puoi usare 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)

Oppure puoi usare 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]

Ma davvero, la tua soluzione attuale va bene. È probabilmente il più ordinato di tutti.


35
+1 per menzionare che c = [a[i] for i in b]va benissimo. Si noti che la itemgettersoluzione non farà la stessa cosa se b ha meno di 2 elementi.
terremoto del

Lato Nota : Utilizzando itemgetter mentre si lavora in multi-processo non funziona. Numpy funziona alla grande in multi-processo.
Lior Magen,

3
Commento aggiuntivo, a[b]funziona solo quando aè un array numpy , cioè lo si crea con una funzione numpy.
Ludwig Zhou,

Ho analizzato le opzioni non intorpidite e itemgetter sembra essere il più veloce, anche leggermente più veloce del semplice scrivere gli indici desiderati tra parentesi, usando Python 3.44
ragardner

@ citizen2077, puoi fare un esempio della sintassi che descrivi?
alancalvitti,

47

alternative:

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

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

il primo è carino perché usi le build-infunzioni
silgon

Il problema con il primo è che __getitem__non sembra essere calcolabile, ad esempio come mappare il tipo di articolo? map(type(a.__getitem__), b)
alancalvitti,

@alancalvitti, lambda x: type(a.__getitem__(x)), b. In questo caso l'utilizzo [..]è più compatto:lambda x: type(a[x]), b
falsetru

9

Un'altra soluzione potrebbe essere tramite la serie Panda:

import pandas as pd

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

È quindi possibile riconvertire c in un elenco se si desidera:

c = list(c)

7

Test di base e non molto approfonditi che confrontano i tempi di esecuzione delle cinque risposte fornite:

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]

utilizzando il seguente input:

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

il semplice ciclo python è stato il più veloce con l'operazione lambda al secondo posto, mapIndexValues ​​e getIndexValues ​​erano costantemente abbastanza simili con il metodo numpy significativamente più lento dopo aver convertito le liste in array numpy. Se i dati sono già in array numpy il metodo numpyIndexValues ​​è stato rimosso più veloce.

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

Non so quale interprete Python usi ma il primo metodo numpyIndexValuesnon funziona da allora a, bsono di tipo range. Immagino che ti interessi convertire a, bper numpy.ndarraysprimo?
strpeter,

@strpeter Sì, non stavo confrontando le mele con le mele, avevo creato array intorpiditi come input nel test case per numpyIndexValues. Ho risolto questo problema ora e tutti usano gli stessi elenchi degli input.
Don Smythe,

4

Sono sicuro che questo è già stato considerato: se la quantità di indici in b è piccola e costante, si potrebbe semplicemente scrivere il risultato come:

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

O ancora più semplice se gli indici stessi sono costanti ...

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

O se esiste un intervallo consecutivo di indici ...

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

Grazie per [a] + [b] = [a, b]
avermelo

3

Ecco un modo più semplice:

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

La mia risposta non usa raccolte numpy o python.

Un modo banale per trovare elementi sarebbe il seguente:

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

Svantaggio: questo metodo potrebbe non funzionare per elenchi più grandi. L'uso di numpy è consigliato per elenchi più grandi.


5
Non c'è bisogno di iterare a. [a[i] for i in b]
falsetru,

1
Questo metodo non funziona nemmeno in nessun altro caso. E se ne aavessero altri 5?
TerryA

IMO, più veloce per fare questo tipo di incrocio usando set
sirgogo

Se sei preoccupato per IndexErrors se b ha numeri che superano la dimensione di a, prova[a[i] if i<len(a) else None for i in b]
576i

0

Indici statici e piccola lista?

Non dimenticare che se l'elenco è piccolo e gli indici non cambiano, come nel tuo esempio, a volte la cosa migliore è usare il disimballaggio della sequenza :

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

Le prestazioni sono molto migliori e puoi anche salvare una riga di codice:

 %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 di modo pitonico:

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

2
Direi che questo è meno "pitonico" di persino l'esempio dell'OP: sei riuscito a trasformare la loro O(n)soluzione in una O(n^2)soluzione, quasi raddoppiando anche la lunghezza del codice. Dovrai anche notare che l'approccio fallirà se l'elenco contiene oggetti sfocati o una parziale uguaglianza, ad esempio se acontiene float('nan'), ciò aumenterà sempre a ValueError.
Brian,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.