获取具有给定索引的 Python 列表的子列表?

Posted

技术标签:

【中文标题】获取具有给定索引的 Python 列表的子列表?【英文标题】:Getting a sublist of a Python list, with the given indices? 【发布时间】:2014-04-20 04:30:04 【问题描述】:

我有一个 Python 列表,比如 a = [0,1,2,3,4,5,6]。我还有一个索引列表,比如b = [0,2,4,5]。如何获取带有b 索引的a 元素列表?

【问题讨论】:

【参考方案1】:

您可以使用 list comprehension 来获取该列表:

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

【讨论】:

也可能是最快的解决方案。 我没想到。它似乎确实快了一点。我会在ipython 中发布我所做的测试。 它也是相当通用的,因为它可以用来从给定键的字典中提取一系列值(这是我最常使用的)。 切片,甚至:itemgetter(slice(2,5))(a) -> [2, 3, 4]。我已经为该函数添加了指向 (2.x) 文档的链接。 列表推导有更多开销,因为迭代是在 Python 中设置和执行的。 operator.itemgetter 在 C 中工作。【参考方案3】:

如果你是functional programming的粉丝,你可以使用maplist.__getitem__

>>> a = [0,1,2,3,4,5,6]
>>> b = [0,2,4,5]
>>> map(a.__getitem__, b)
[0, 2, 4, 5]
>>>

虽然列表理解方法在 Python 中更为规范...

【讨论】:

【参考方案4】:

如果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 生成索引、值对的元组。由于我们同时拥有 item 及其索引,因此我们可以检查 b 中是否存在索引

【讨论】:

【参考方案5】:

所有提到的方法和来自Python dictionary: Get list of values for list of keys的其他方法的速度比较:

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() 中。 @kundor 是的,关于filter,如果列表中没有唯一值,您是对的。 @Kundor,至于包装在list - 因为这是 Python 2.7,所以没有必要。 那为什么要把filter包裹在list中呢?【参考方案6】:

使用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]

【讨论】:

【参考方案7】:

使用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]

【讨论】:

【参考方案8】:

如果这对您很重要,还有另一种更好的性能替代方案 - 它绝不是最 Pythonic,但我很确定它是最有效的:

>>> list(filter(lambda x: a.index(x) in b, a))
[0, 2, 4, 5]

注意:在 Python 2 中您不需要转换为 list。但是在 Python 3 之后的版本中您需要转换(如果未来的访问者可能有类似的问题)。

【讨论】:

由于 OP 使用的是 Python 2.7,因此您无需将 filter 放在 list 中。这仅在 Python 3.x 中。 @iCodez 谢谢,我已经扩展了我的答案。我将其转换为list 以测试我的解决方案(我正在使用 Python 3)- 但是我认为我会保留我的解决方案,因为它不会在 Python 2 中导致错误,同时适用于更广泛的 Python 3 受众.

以上是关于获取具有给定索引的 Python 列表的子列表?的主要内容,如果未能解决你的问题,请参考以下文章

Python 二维列表 如何获取有关某数据最大/小值对应的子列表?

具有任意索引的 Python 列表切片

在Python中按属性获取对象列表中的索引

Prolog:获取包含给定整数的子列表

将多索引数据帧的索引值提取为python中的简单列表

如何获取给定文件夹中具有特定扩展名的文件列表?