我需要从给定的列表中选择一些元素,知道它们的索引。假设我想创建一个新列表,其中包含从给定列表[- 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] ?
我需要从给定的列表中选择一些元素,知道它们的索引。假设我想创建一个新列表,其中包含从给定列表[- 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] ?
当前回答
静态索引和小列表?
不要忘记,如果列表很小,并且索引没有改变,就像你的例子中,有时最好的方法是使用序列解包:
_,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
其他回答
另一个解决方案是通过熊猫系列:
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)
选择:
>>> map(a.__getitem__, b)
[1, 5, 5]
>>> import operator
>>> operator.itemgetter(*b)(a)
(1, 5, 5)
有点像python的方式:
c = [x for x in a if a.index(x) in b]
这里有一个更简单的方法:
a = [-2,1,5,3,8,5,6]
b = [1,2,5]
c = [e for i, e in enumerate(a) if i in b]
列表理解显然是最直接和最容易记住的——除了相当python化!
在任何情况下,在提出的解决方案中,它不是最快的(我已经在Windows上使用Python 3.8.3运行了我的测试):
import timeit
from itertools import compress
import random
from operator import itemgetter
import pandas as pd
__N_TESTS__ = 10_000
vector = [str(x) for x in range(100)]
filter_indeces = sorted(random.sample(range(100), 10))
filter_boolean = random.choices([True, False], k=100)
# Different ways for selecting elements given indeces
# list comprehension
def f1(v, f):
return [v[i] for i in filter_indeces]
# itemgetter
def f2(v, f):
return itemgetter(*f)(v)
# using pandas.Series
# this is immensely slow
def f3(v, f):
return list(pd.Series(v)[f])
# using map and __getitem__
def f4(v, f):
return list(map(v.__getitem__, f))
# using enumerate!
def f5(v, f):
return [x for i, x in enumerate(v) if i in f]
# using numpy array
def f6(v, f):
return list(np.array(v)[f])
print("{:30s}:{:f} secs".format("List comprehension", timeit.timeit(lambda:f1(vector, filter_indeces), number=__N_TESTS__)))
print("{:30s}:{:f} secs".format("Operator.itemgetter", timeit.timeit(lambda:f2(vector, filter_indeces), number=__N_TESTS__)))
print("{:30s}:{:f} secs".format("Using Pandas series", timeit.timeit(lambda:f3(vector, filter_indeces), number=__N_TESTS__)))
print("{:30s}:{:f} secs".format("Using map and __getitem__", timeit.timeit(lambda: f4(vector, filter_indeces), number=__N_TESTS__)))
print("{:30s}:{:f} secs".format("Enumeration (Why anyway?)", timeit.timeit(lambda: f5(vector, filter_indeces), number=__N_TESTS__)))
我的结果是:
列表理解:0.007113秒 操作符。Itemgetter:0.003247秒 使用Pandas系列:2.977286秒 使用map和getitem:0.005029秒 枚举(为什么?):0.135156秒 Numpy:0.157018秒