我有一个列表,我正在搜索以找到重复的索引。
listA = [[1,2,3],[3,4,5],[6,7,8],[1,2,3]]
listA_set = [[1,2,3],[3,4,5],[6,7,8]]
enum_listA = enumerate(listA)
listA_indices = []
for i in listA_set:
listA_indices([j[0] for j in enum_listA if j[1] == i])
预期结果是:
listA_indices = [[0,3],[1],[2]]
但相反,我得到了:
listA_indices = [[0,3],[],[]]
如果我在in-line for循环中包含枚举(参见下面的示例),我会收到正确的答案,但会看到速度显着降低。如何在不丢失存储在enum_listA中的枚举信息的情况下执行此任务?
for i in listA_set:
listA_indices([j[0] for j in enumerate(listA) if j[1] == i])
答案 0 :(得分:2)
enumerate
返回一个只能迭代一次的迭代器。因此,一旦它耗尽,在第一次for循环迭代的列表理解中迭代它之后就会出现这种情况,它将不再产生任何项目。
如果要保留信息,则必须将该数据实际存储在内存中,例如列表:
enum_listA = list(enumerate(listA))
请注意,这会有效地复制列表中的信息并添加索引,因此这会浪费大量额外内存,并且最终可能不会比重复使用枚举对象更有效。
然而,您看到的性能差异来自以下事实:在第一次循环迭代之后,枚举数为空,因此列表推导不再为后续迭代运行。
答案 1 :(得分:1)
使用dict
和set
,您的代码将更有效地运行:
from collections import defaultdict
st = set(map(tuple, listA_set))
d = defaultdict(list)
for i, ele in enumerate(map(tuple,listA)):
if ele in st:
d[ele].append(i)
print(list(d.items()))
print(list(d.values()))
输出:
[((3, 4, 5), [1]), ((6, 7, 8), [2]), ((1, 2, 3), [0, 3])]
[[1], [2], [0, 3]]
如果您想维持首次看到的订单:
from collections import OrderedDict
d = OrderedDict()
for i, ele in enumerate(map(tuple, listA)):
if ele in st:
d.setdefault(ele, []).append(i)
print(list(d.items()))
print(list(d.values()))
输出:
[((1, 2, 3), [0, 3]), ((3, 4, 5), [1]), ((6, 7, 8), [2])]
[[0, 3], [1], [2]]
无论你使用枚举的方式,你的复杂性仍然是二次方的,这将比你自己的方法快得多。
随机数据集的某些时间:
In [27]: from random import randint
In [28]: listA_set = [[randint(1,20) for _ in range(10)] for _ in range(2000)]
In [29]: listA = [[randint(1,20) for _ in range(10)] for _ in range(3000)]
In [30]: %%timeit
listA_indices = []
for i in listA_set:
listA_indices.append([j[0] for j in enumerate(listA) if j[1] == i])
....:
1 loops, best of 3: 696 ms per loop
In [31]: %%timeit
st = set(map(tuple, listA_set))
from collections import OrderedDict
d = OrderedDict()
for i, ele in enumerate(map(tuple,listA)):
if ele in st:
d.setdefault(ele, []).append(i)
....:
1000 loops, best of 3: 1.49 ms per loop
~400
次更快。
答案 2 :(得分:0)
您看到速度显着降低(根据您的术语)的原因是您的第二个正确版本实际上正在计算您正在寻找的值。
因为枚举产生一个生成器,一旦你消耗它的值,你就不能再生成它们了。您提供的第二个版本是使用枚举的正确方法。
方面没有,我不确定你的代码是如何工作的。您正在调用列表,一个不可调用的对象
listA_indices([...])
也许,你打算做
listA_indices += [...]