У меня есть список Python, скажем a = [0,1,2,3,4,5,6]
. У меня также есть список индексов, скажем b = [0,2,4,5]
. Как получить список элементов a
с индексами в b
?
Получение подсписок в списке Python с указанными индексами?
Ответ 1
Вы можете использовать список для получения списка:
c = [a[index] for index in b]
print c
Это эквивалентно:
c= []
for index in b:
c.append(a[index])
print c
Вывод:
[0,2,4,5]
Примечание:
Помните, что some_list[index]
- это нотация, используемая для доступа к элементу list
в определенном индексе.
Ответ 2
Что-то другое...
>>> a = range(7)
>>> b = [0,2,4,5]
>>> import operator
>>> operator.itemgetter(*b)(a)
(0, 2, 4, 5)
Функция itemgetter
принимает один или несколько ключей в качестве аргументов и возвращает функцию, которая возвращает элементы в заданных ключах в своем аргумент. Таким образом, в приведенном выше примере мы создаем функцию, которая возвращает элементы в индексе 0, индекс 2, индекс 4 и индекс 5, затем применяет эту функцию к a
.
Кажется, это немного быстрее, чем понимание эквивалентного списка
In [1]: import operator
In [2]: a = range(7)
In [3]: b = [0,2,4,5]
In [4]: %timeit operator.itemgetter(*b)(a)
1000000 loops, best of 3: 388 ns per loop
In [5]: %timeit [ a[i] for i in b ]
1000000 loops, best of 3: 415 ns per loop
In [6]: f = operator.itemgetter(*b)
In [7]: %timeit f(a)
10000000 loops, best of 3: 183 ns per loop
Что касается того, почему itemgetter
работает быстрее, понимание должно выполнять дополнительные байтовые коды Python.
In [3]: def f(a,b): return [a[i] for i in b]
In [4]: def g(a,b): return operator.itemgetter(*b)(a)
In [5]: dis.dis(f)
1 0 BUILD_LIST 0
3 LOAD_FAST 1 (b)
6 GET_ITER
>> 7 FOR_ITER 16 (to 26)
10 STORE_FAST 2 (i)
13 LOAD_FAST 0 (a)
16 LOAD_FAST 2 (i)
19 BINARY_SUBSCR
20 LIST_APPEND 2
23 JUMP_ABSOLUTE 7
>> 26 RETURN_VALUE
Хотя itemgetter
- это один вызов, реализованный в C:
In [6]: dis.dis(g)
1 0 LOAD_GLOBAL 0 (operator)
3 LOAD_ATTR 1 (itemgetter)
6 LOAD_FAST 1 (b)
9 CALL_FUNCTION_VAR 0
12 LOAD_FAST 0 (a)
15 CALL_FUNCTION 1
18 RETURN_VALUE
Ответ 3
Если вы являетесь поклонником функционального программирования, вы можете использовать map
и list.__getitem__
:
>>> a = [0,1,2,3,4,5,6]
>>> b = [0,2,4,5]
>>> map(a.__getitem__, b)
[0, 2, 4, 5]
>>>
Подход к пониманию списка более каноничен в Python, хотя...
Ответ 4
Используя List Comprehension, это должно работать -
li = [a[i] for i in b]
Тестирование этого -
>>> a = [0,10,20,30,40,50,60]
>>> b = [0,2,4,5]
>>> li = [a[i] for i in b]
>>> li
[0, 20, 40, 50]
Ответ 5
Немного сравнения скорости для всех упомянутых методов и других из Python dictionary: Получить список значений для списка ключей:
Python 2.7.11 |Anaconda 2.4.1 (64-bit)| (default, Jan 19 2016, 12:08:31) [MSC v.1500 64 bit (AMD64)] on win32
In[2]: import numpy.random as nprnd
idx = nprnd.randint(1000, size=10000)
l = nprnd.rand(1000).tolist()
from operator import itemgetter
import operator
f = operator.itemgetter(*idx)
%timeit f(l)
%timeit list(itemgetter(*idx)(l))
%timeit [l[_] for _ in idx] # list comprehension
%timeit map(l.__getitem__, idx)
%timeit list(l[_] for _ in idx) # a generator expression passed to a list constructor.
%timeit map(lambda _: l[_], idx) # using 'map'
%timeit [x for i, x in enumerate(l) if i in idx]
%timeit filter(lambda x: l.index(x) in idx, l) # UPDATE @Kundor: work only for list with unique elements
10000 loops, best of 3: 175 µs per loop
1000 loops, best of 3: 707 µs per loop
1000 loops, best of 3: 978 µs per loop
1000 loops, best of 3: 1.03 ms per loop
1000 loops, best of 3: 1.18 ms per loop
1000 loops, best of 3: 1.86 ms per loop
100 loops, best of 3: 12.3 ms per loop
10 loops, best of 3: 21.2 ms per loop
Итак, самый быстрый f = operator.itemgetter(*idx); f(l)
Ответ 6
Многие из предлагаемых решений будут создавать KeyError
, если b
содержит индекс, отсутствующий в a
. Следующие ниже будут пропускать недопустимые индексы, если это необходимо.
>>> b = [0,2,4,5]
>>> a = [0,1,2,3,4,5,6]
>>> [x for i,x in enumerate(a) if i in b]
[0, 2, 4, 5]
>>> b = [0,2,4,500]
>>> [x for i,x in enumerate(a) if i in b]
[0, 2, 4]
enumerate
создает кортежи индексов, пары значений. Поскольку у нас есть как элемент, так и его индекс, мы можем проверить наличие индекса в b
Ответ 7
Еще одна альтернатива для лучшей производительности, если это важно для вас, - это далеко не самый Pythonic, но я уверен, что он наиболее эффективен:
>>> list(filter(lambda x: a.index(x) in b, a))
[0, 2, 4, 5]
Примечание. Вам не нужно преобразовывать в list
в Python 2. Однако вы делаете это в Python 3 и далее (если любые будущие посетители могут иметь аналогичную проблему).
Ответ 8
Использование numpy.asarray
. Numpy позволяет получать подматрица массива по списку индексов.
>>> import numpy as np
>>> a = [0,10,20,30,40,50,60]
>>> b = [0,2,4,5]
>>> res = np.asarray(a)[b].tolist()
>>> res
[0, 20, 40, 50]