Доступ к нескольким элементам списка, зная их индекс


233

Мне нужно выбрать несколько элементов из данного списка, зная их индекс. Допустим, я хотел бы создать новый список, который содержит элемент с индексом 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функцию.
Уилл

Ответы:


219

Вы можете использовать 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]

Но на самом деле ваше текущее решение в порядке. Это, наверное, самое лучшее из всех.


36
+1 за упоминание, что c = [a[i] for i in b]это прекрасно. Обратите внимание, что itemgetterрешение не будет делать то же самое, если b имеет менее 2 элементов.
землетрясение

Side Примечание : Использование itemgetter при работе в многозадачных не работает. Numpy прекрасно работает в многопроцессорном режиме.
Лиор Маген

3
Дополнительный комментарий, a[b]работает только тогда, когда aэто массив NumPy , т.е. вы создаете его с помощью функции NUMPY.
Людвиг Чжоу

Я протестировал непустые параметры, и элементгетер кажется самым быстрым, даже немного быстрее, чем просто ввод нужных индексов в скобках, используя 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
вариант

Проблема в том, что первая проблема заключается в том, что __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]
onewhaleid


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]

Недостаток: этот метод может не работать для больших списков. Использование больших номеров рекомендуется для больших списков.


5
Нет необходимости повторять a. [a[i] for i in b]
Falsetru

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
Я бы сказал, что это менее «питонно», чем даже пример OP - вам удалось превратить их O(n)решение в O(n^2)решение, почти удвоив длину кода. Вы также захотите отметить, что подход потерпит неудачу, если список содержит объекты, будет нечеткое или частичное равенство, например, если aсодержит float('nan'), это всегда вызовет a ValueError.
Брайан
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.