Greifen Sie auf mehrere Elemente der Liste zu, wobei Sie deren Index kennen


232

Ich muss einige Elemente aus der angegebenen Liste auswählen und deren Index kennen. Angenommen, ich möchte aus der angegebenen Liste eine neue Liste erstellen, die ein Element mit dem Index 1, 2, 5 enthält [-2, 1, 5, 3, 8, 5, 6]. Was ich getan habe ist:

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

Gibt es einen besseren Weg, dies zu tun? so etwas wie c = a [b]?


1
Übrigens habe ich hier eine andere Lösung gefunden. Ich habe es noch nicht getestet, aber ich denke , ich poste kann es hier , wenn Sie interessiert sind code.activestate.com/recipes/...
hoang tran

Das ist die gleiche Lösung wie in der Frage erwähnt, aber in eine lambdaFunktion eingeschlossen.
Will Dereham

Antworten:


218

Sie können verwenden 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)

Oder Sie können numpy verwenden :

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]

Aber wirklich, Ihre aktuelle Lösung ist in Ordnung. Es ist wahrscheinlich das ordentlichste von allen.


35
+1 für die Erwähnung, dass c = [a[i] for i in b]das vollkommen in Ordnung ist. Beachten Sie, dass die itemgetterLösung nicht dasselbe tut, wenn b weniger als 2 Elemente enthält.
Flornbeben

Side Hinweis : Die Verwendung itemgetter während in Multi-Prozess - Arbeit funktioniert nicht. Numpy funktioniert hervorragend in Multiprozessen.
Lior Magen

3
Zusätzlicher Kommentar, a[b]funktioniert nur, wenn aes sich um ein Numpy- Array handelt, dh wenn Sie es mit einer Numpy-Funktion erstellen.
Ludwig Zhou

Ich habe die nicht numpy Optionen verglichen und itemgetter scheint die schnellste zu sein, sogar etwas schneller als einfach die gewünschten Indizes in Klammern mit Python
3.44

@ Citizen2077, können Sie ein Beispiel für die von Ihnen beschriebene Syntax geben?
Alancalvitti

47

Alternativen:

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

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

Der erste ist schön, weil Sie build-inFunktionen verwenden
Silgon

Das Problem mit dem ersten ist, dass __getitem__es nicht kompatibel zu sein scheint, z. B. wie man den Typ des Elements abbildet. map(type(a.__getitem__), b)
Alancalvitti

@alancalvitti , lambda x: type(a.__getitem__(x)), b. In diesem Fall ist die Verwendung [..]kompakter:lambda x: type(a[x]), b
Falsetru

9

Eine andere Lösung könnte über die Pandas-Serie sein:

import pandas as pd

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

Sie können c dann wieder in eine Liste konvertieren, wenn Sie möchten:

c = list(c)

7

Grundlegende und nicht sehr umfangreiche Tests zum Vergleich der Ausführungszeit der fünf gelieferten Antworten:

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]

mit der folgenden Eingabe:

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

Die einfache Python-Schleife war die schnellste mit der Lambda-Operation, eine knappe Sekunde, mapIndexValues ​​und getIndexValues ​​waren sich mit der numpy-Methode nach der Konvertierung von Listen in numpy-Arrays durchweg ziemlich ähnlich. Wenn sich Daten bereits in numpy-Arrays befinden, ist die numpyIndexValues-Methode mit der entfernten numpy.array-Konvertierung am schnellsten.

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

Ich weiß nicht , was Python - Interpreter Sie verwenden , aber die erste Methode numpyIndexValuesnicht funktioniert , da a, bvom Typ ist range. Ich vermute , dass Sie konvertieren ment a, bum numpy.ndarrayszuerst?
Strpeter

@strpeter Ja, ich habe Äpfel nicht mit Äpfeln verglichen, sondern im Testfall für die numpyIndexValues ​​Numpy-Arrays als Eingabe erstellt. Ich habe dies jetzt behoben und alle verwenden die gleichen Listen als Eingabe.
Don Smythe

4

Ich bin sicher, dass dies bereits berücksichtigt wurde: Wenn die Anzahl der Indizes in b klein und konstant ist, könnte man das Ergebnis einfach wie folgt schreiben:

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

Oder noch einfacher, wenn die Indizes selbst Konstanten sind ...

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

Oder wenn es einen aufeinanderfolgenden Bereich von Indizes gibt ...

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

Vielen Dank, dass Sie mich daran erinnert haben[a] + [b] = [a, b]
onewhaleid

3

Hier ist ein einfacher Weg:

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

Meine Antwort verwendet keine Numpy- oder Python-Sammlungen.

Ein trivialer Weg, um Elemente zu finden, wäre wie folgt:

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

Nachteil: Diese Methode funktioniert möglicherweise nicht für größere Listen. Die Verwendung von numpy wird für größere Listen empfohlen.


5
Keine Notwendigkeit zu iterieren a. [a[i] for i in b]
Falsetru

1
Diese Methode funktioniert in keinem anderen Fall. Was wäre, wenn anoch 5 drin wären ?
TerryA

IMO, schneller, um diese Art von Kreuzung mit Mengen
Sirgogo

Wenn Sie über IndexErrors besorgt sind, wenn b Zahlen hat, die die Größe von a überschreiten, versuchen Sie[a[i] if i<len(a) else None for i in b]
576i

0

Statische Indizes und kleine Liste?

Vergessen Sie nicht, dass, wenn die Liste klein ist und sich die Indizes nicht ändern, wie in Ihrem Beispiel, manchmal das Entpacken von Sequenzen am besten ist :

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

Die Leistung ist viel besser und Sie können auch eine Codezeile speichern:

 %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

Art von pythonischer Art:

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

2
Ich würde sagen, dies ist weniger "pythonisch" als das Beispiel des OP - Sie haben es geschafft, ihre O(n)Lösung in eine O(n^2)Lösung umzuwandeln und gleichzeitig die Länge des Codes fast zu verdoppeln. Sie sollten auch beachten, dass der Ansatz fehlschlägt, wenn die Liste Objekte enthält, die unscharf oder teilweise gleich sind, z. B. wenn sie aenthält float('nan'), wird immer a ausgelöst ValueError.
Brian
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.