我有一个Python列表,比如说a=[0,1,2,3,4,5,6]
。我还有一个索引列表,比如说b=[0,2,4,5]
。如何获取a
中的索引在b
中的元素的列表?
我有一个Python列表,比如说a=[0,1,2,3,4,5,6]
。我还有一个索引列表,比如说b=[0,2,4,5]
。如何获取a
中的索引在b
中的元素的列表?
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]
是用于访问列表中特定索引处的元素的标记符号。
有所不同的东西...
>>> 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
如果你是函数式编程的粉丝,你可以使用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中,列表推导式的方法更加规范...
许多提出的解决方案会在 b 中包含a
中不存在的索引时产生 KeyError
。如果需要,以下代码将跳过无效的索引。
>>> 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 中。
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)
l
是 [1,2,3,2,1,2,3,2]
,而 idx
是 [0,1,4,5]
,那么过滤方法将给出 [1, 2, 2, 1, 2, 2]
,而所有其他方法将(正确地)给出 [1,2,1,2]
。此外,为了保持一致性,您应该在 map
调用中包装 list()
。 - Nick Matteofilter
的看法是正确的。 - Sklavitlist
中包装 - 这不是必要的,因为这是Python 2.7。 - Sklavitlist
包装filter
呢? - Nick Matteonumpy.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]
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]
>>> list(filter(lambda x: a.index(x) in b, a))
[0, 2, 4, 5]
list
。但是在Python 3及以后版本中需要(如果有任何未来访问者可能遇到类似问题)。list
以测试我的解决方案(我正在使用Python 3)-但是我认为我会将我的解决方案留在那里,因为它不会在Python 2中引起错误,同时适用于更广泛的Python 3受众。 - anon582847382
ipython
中发布我所做的测试。 - chepneritemgetter(slice(2,5))(a)
->[2, 3, 4]
。我已经添加了该函数的(2.x)文档链接。 - chepneroperator.itemgetter
则是在C中完成其工作。 - chepner