Доступ до декількох елементів списку, знаючи їх індекс


232

Мені потрібно вибрати деякі елементи із даного списку, знаючи їх індекс. Скажімо, я хотів би створити новий список, який містить елемент із індексом 1, 2, 5, із даного списку [-2, 1, 5, 3, 8, 5, 6]. Що я зробив:

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

Чи є кращий спосіб це зробити? щось на зразок c = a [b]?


1
до речі, я знайшов тут інше рішення. Я ще не перевіряв її, але думаю, що можу опублікувати її тут, коли вас зацікавить code.activestate.com/recipes/…
hoang tran

Це те саме рішення, що згадувалося у питанні, але закріплене у lambdaфункції.
Буде Дерехам

Відповіді:


218

Ви можете використовувати 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)

Або ви можете використовувати 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]

Але дійсно, ваше поточне рішення чудово. Це, мабуть, найновіший з усіх.


35
+1, щоб згадати, що c = [a[i] for i in b]це прекрасно. Зауважте, що itemgetterрішення не зробить те саме, якщо b має менше 2 елементів.
flornquake

Бічна примітка : Використання itemgetter під час роботи в декількох процесах не працює. Numpy чудово працює в багатопроцесорних процесах.
Ліор Маген

3
Додатковий коментар, a[b]працює лише тоді, коли aє масив numpy , тобто ви створюєте його за допомогою функції numpy.
Людвіг Чжоу

Я визначив параметри, що не мають нумету, і itemgetter видається найшвидшим, навіть трохи швидшим, ніж просто набрати потрібні індекси всередині дужок, використовуючи Python 3.44
ragardner

@ Citizen2077, чи можете ви навести приклад описаного вами синтаксису?
alancalvitti

47

Альтернативи:

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

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

перший приємний тим, що ви використовуєте build-inфункції
silgon

Проблема з першою полягає в тому, що __getitem__вона, здається, не сумісна, наприклад, як відобразити тип елемента? map(type(a.__getitem__), b)
alancalvitti

@alancalvitti, lambda x: type(a.__getitem__(x)), b. У цьому випадку використання [..]є більш компактним:lambda x: type(a[x]), b
falsetru

9

Інше рішення може бути через серію панд:

import pandas as pd

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

Потім ви можете перетворити c назад у список, якщо хочете:

c = list(c)

7

Основне і не дуже обширне тестування, порівнюючи час виконання п'яти наданих відповідей:

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]

використовуючи наступний вхід:

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

простий цикл python був найшвидшим, коли операція лямбда була близькою секундою, mapIndexValues ​​і getIndexValues ​​постійно були схожі на метод numpy значно повільніше після перетворення списків у numpy масиви. Якщо дані вже є в numpy масивах, метод numpyIndexValues ​​видалено з перетворенням numpy.array найшвидший.

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

Я не знаю , що Python інтерпретатора використовується , але перший метод numpyIndexValuesне працює , так як a, bмають типу range. Я припускаю , що ви Мент звернений a, bв numpy.ndarraysпершу чергу?
strpeter

@strpeter Так. Я не порівнював яблука з яблуками, я створив масиви numpy як вхідні дані в тестовому випадку для numpyIndexValues. Я це виправив зараз і всі використовують ті ж списки, що і вхідні дані.
Дон Сміт

4

Я впевнений, що це вже було враховано: Якщо кількість індексів в b невелика і незмінна, можна просто записати результат на зразок:

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

Або навіть простіше, якщо самі індекси є константами ...

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

Або якщо є послідовний діапазон індексів ...

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

Дякую за те, що нагадали мені про це[a] + [b] = [a, b]
кіт


1

У моїй відповіді не використовуються колекції numpy чи python.

Одним тривіальним способом пошуку елементів є такий:

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

Недолік: Цей метод може не працювати для більш великих списків. Використовувати numpy рекомендується для більших списків.


5
Не потрібно повторювати a. [a[i] for i in b]
фальсетру

1
Цей метод навіть не працює в будь-якому іншому випадку. Що робити, якщо aв ньому було ще 5?
TerryA

ІМО, швидше зробити цей вид перехрестя за допомогою наборів
sirgogo

Якщо ви переживаєте за IndexErrors, якщо b має цифри, що перевищують розмір a, спробуйте[a[i] if i<len(a) else None for i in b]
576i

0

Статичні індекси та невеликий список?

Не забувайте, що якщо список невеликий, а індекси не змінюються, як у вашому прикладі, іноді найкраще використовувати розпакування послідовностей :

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

Продуктивність значно краща, і ви також можете зберегти один рядок коду:

 %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

Вид пітонічного способу:

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

2
Я б сказав, що це менш "пітонічно", ніж навіть приклад ОП - вам вдалося перетворити їх O(n)рішення на O(n^2)рішення, а також майже подвоївши довжину коду. Ви також хочете відзначити, що підхід не вдасться, якщо список містить об'єкти нечіткі або часткові рівності, наприклад, якщо він aмістить float('nan'), це завжди підвищить a ValueError.
Брайан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.