访问列表的多个元素,知道它们的索引


232

我需要从给定列表中选择一些元素,知道它们的索引。假设我要创建一个新列表,该列表包含给定列表[-2、1、5、3、8、5、6]中索引为1、2、5的元素。我所做的是:

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
...

这与问题中提到的解决方案相同,但是包含在一个lambda函数中。
Will Dereham

Answers:


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在多进程中效果很好。
Lior Magen

3
附加注释,当是numpy数组时才a[b]起作用,即使用numpy函数创建它。a
路德维希·周

我已经对非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

另一个解决方案可以通过pandas Series:

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循环是使用lambda操作最快的一秒钟,紧随其后的是,mapIndexValues和getIndexValues始终与numpy方法相似,将列表转换为numpy数组后速度显着降低。最快的。

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不起作用,因为ab有型range。我猜你彪转换abnumpy.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 '17

3

这是一个更简单的方法:

a = [-2,1,5,3,8,5,6]
b = [1,2,5]
c = [e for i, e in enumerate(a) if i in 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]
falsetru 2014年

1
此方法在任何其他情况下均无效。如果其中a还有5个呢?
TerryA

IMO,使用集合
sirgogo

如果您担心b的数字超过a的大小而发生IndexErrors,请尝试[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

一种pythonic方式:

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

2
我要说的是,这甚至比OP的示例还不那么“ pythonic”-您已经设法将他们的O(n)解决方案变成了O(n^2)解决方案,同时代码长度也几乎翻了一番。您还将要注意,如果列表包含的对象模糊或部分相等,则方法将失败,例如,如果a包含float('nan'),则将始终引发a ValueError
布赖恩
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.