Uzyskaj dostęp do wielu elementów listy, znając ich indeks

232

Muszę wybrać niektóre elementy z podanej listy, znając ich indeks. Powiedzmy, że chciałbym utworzyć nową listę, która zawiera element o indeksie 1, 2, 5, z podanej listy [-2, 1, 5, 3, 8, 5, 6]. To co zrobiłem to:

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

Czy jest na to lepszy sposób? coś jak c = a [b]?

hoang tran
źródło
1
przy okazji, znalazłem tutaj inne rozwiązanie. Jeszcze go nie przetestowałem, ale myślę, że mogę go tutaj opublikować, gdy tylko zainteresuje Cię code.activestate.com/recipes/…
hoang tran
To jest to samo rozwiązanie, co wspomniane w pytaniu, ale zawarte w lambdafunkcji.
Will Dereham,

Odpowiedzi:

218

Możesz użyć 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)

Lub możesz użyć 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]

Ale tak naprawdę twoje obecne rozwiązanie jest w porządku. To chyba najładniejszy ze wszystkich.

TerryA
źródło
35
+1 za wspomnienie, że c = [a[i] for i in b]jest w porządku. Zauważ, że itemgetterrozwiązanie nie zrobi tego samego, jeśli b ma mniej niż 2 elementy.
trzęsienie ziemi
Side Uwaga : Używanie itemgetter podczas pracy w multi-proces nie działa. Numpy działa świetnie w wielu procesach.
Lior Magen
3
Dodatkowy komentarz a[b]działa tylko wtedy, gdy ajest tablicą numpy , tzn. Tworzysz ją za pomocą funkcji numpy.
Ludwig Zhou,
Przeprowadziłem testy porównawcze opcji innych niż numpy, a element itemter wydaje się najszybszy, nawet nieco szybszy niż zwykłe wpisywanie pożądanych indeksów w nawiasach, używając Pythona 3.44
ragardner,
@ citizen2077, czy możesz podać przykład opisanej składni?
alancalvitti
47

Alternatywy:

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

>>> import operator
>>> operator.itemgetter(*b)(a)
(1, 5, 5)
falsetru
źródło
pierwszy jest fajny, ponieważ używasz build-infunkcji
silgon
Problem w pierwszym polega na tym, __getitem__że wydaje się, że nie da się go zmierzyć, np. Jak zmapować typ przedmiotu? map(type(a.__getitem__), b)
alancalvitti
@alancalvitti, lambda x: type(a.__getitem__(x)), b. W tym przypadku użycie [..]jest bardziej kompaktowe:lambda x: type(a[x]), b
falsetru
9

Innym rozwiązaniem może być seria pand:

import pandas as pd

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

Następnie możesz przekonwertować c z powrotem na listę, jeśli chcesz:

c = list(c)
BossaNova
źródło
7

Podstawowe i niezbyt obszerne testy porównujące czas wykonania pięciu dostarczonych odpowiedzi:

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]

przy użyciu następującego wejścia:

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

prosta pętla pytona była najszybsza z operacją lambda w ułamku sekundy, mapIndexValues ​​i getIndexValues ​​były konsekwentnie dość podobne z metodą numpy znacznie wolniej po konwersji list na tablice numpy.Jeśli dane są już w tablicach numpy, metoda numpyIndexValues ​​z usuniętą konwersją numpy.array jest najszybszy.

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
źródło
I nie wiem, co interpreter Pythona użyć ale pierwsza metoda numpyIndexValuesnie działa, ponieważ a, bsą typu range. Domyślam się, że ment do konwersji a, baby numpy.ndarraysw pierwszej kolejności?
strpeter
@strpeter Tak Nie porównywałem jabłek z jabłkami, stworzyłem tablice numpy jako dane wejściowe w przypadku testowym dla wartości numpyIndexValues. Naprawiłem to teraz i wszyscy używają tych samych list jako danych wejściowych.
Don Smythe
4

Jestem pewien, że zostało to już wzięte pod uwagę: Jeśli liczba wskaźników wb jest niewielka i stała, można po prostu zapisać wynik w następujący sposób:

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

Lub nawet prościej, jeśli same wskaźniki są stałymi ...

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

Lub jeśli istnieje kolejny zakres indeksów ...

c = a[1:3] + [a[5]]
ecp
źródło
Dziękuję za przypomnienie mi, że[a] + [b] = [a, b]
onewhaleid
3

Oto prostszy sposób:

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
źródło
1

Moja odpowiedź nie korzysta z kolekcji numpy ani python.

Jednym trywialnym sposobem znajdowania elementów byłoby:

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

Wada: ta metoda może nie działać w przypadku większych list. Użycie numpy jest zalecane w przypadku większych list.

Lavya „Orion”
źródło
5
Nie trzeba iterować a. [a[i] for i in b]
falsetru
1
Ta metoda nawet nie działa w żadnym innym przypadku. A gdyby abyło jeszcze 5?
TerryA
IMO, szybciej zrobić tego rodzaju skrzyżowanie za pomocą zestawów
sirgogo
Jeśli martwisz się błędami IndexErrors, jeśli b ma liczby przekraczające rozmiar a, spróbuj[a[i] if i<len(a) else None for i in b]
576i
0

Indeksy statyczne i mała lista?

Nie zapominaj, że jeśli lista jest niewielka, a indeksy się nie zmieniają, jak w twoim przykładzie, czasami najlepszą rzeczą jest użycie rozpakowywania sekwencji :

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

Wydajność jest znacznie lepsza i można również zapisać jeden wiersz kodu:

 %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
źródło
0

Rodzaj pythonowego sposobu:

c = [x for x in a if a.index(x) in b]
Dmitrij K. Czebanow
źródło
2
Powiedziałbym, że jest to mniej „pytoniczne” niż nawet przykład OP - udało ci się przekształcić ich O(n)rozwiązanie w O(n^2)rozwiązanie, a jednocześnie prawie podwoić długość kodu. Warto również zauważyć, że podejście zakończy się niepowodzeniem, jeśli lista zawiera obiekty będzie rozmyta lub częściowa równość, np. Jeśli azawiera float('nan'), to zawsze podniesie a ValueError.
Brian