我有一个巨大的对象列表:(大约500k elts)。
class Signal:
def __init__(self, fq, t0, tf):
self.fq = fq
self.t0 = t0
self.tf = tf
def __eq__(self, s):
""" == comparison method."""
return self.fq == s.fq
def __ne__(self, s):
""" != comparison method."""
return not self.__eq__(self, s)
t0, tf = 0, 200
signals = [[S(f1, t0, tf), S(f2, t0, tf), S(f3, t0, tf), S(f4, t0, tf), S(f5, t0, tf), S(f6, t0, tf)]
for f1 in frequencies for f2 in frequencies for f3 in frequencies for f4 in frequencies
for f5 in frequencies for f6 in frequencies]
我的程序映射列表并生成.pkl
个文件,其中包含列表中每个元素的特定名称。
def file_namer(signals):
frequencies = tuple([s.fq for s in signals])
return "F{}.pkl".format(frequencies)
列表中的一些元素已经计算好了,或者,计算了这个元素的排列,因此我想在映射之前删除它们。
import itertools
import os
folder = "folder_of_the_pkl"
files = os.listdir(folder)
def is_computed(files, s):
possibilities = list()
for elt in itertools.permutations(s):
possibilities.append(file_namer(s))
if any([name in files for name in possibilities]):
return True
else:
return False
s_to_remove = list()
for s in signals:
if is_computed(files, s):
s_to_remove.append(s)
for elt in s_to_remove:
signals.remove(elt)
这就是我想出来的。这是相当无效的,我很高兴看到你提出改进的建议!
谢谢!
N.B:这是我程序的一个相当简化的版本。对象更重(10+个参数)。
答案 0 :(得分:1)
我建议您不要从列表中删除。建立另一个:
signals_ = list()
for s in signals:
if not is_computed(files, s):
signals_.append(s)
signals = signals_
然后我会查看你的is_computed
函数,看看是否可以避免构建可能性列表:
def is_computed(files, s):
for elt in itertools.permutations(s):
name = file_namer(s)
if name in files:
return True
return False
如果name in files
是files
,则测试set
会更快。
更好的是:
解析每个文件名,例如:
>>> "F{}.pkl".format((1,2,3))
'F(1, 2, 3).pkl'
回到元组:
>>> import ast
>>> ast.literal_eval('F(1, 2, 3).pkl'[1:].split('.')[0])
(1, 2, 3)
然后,您可以通过排序(1,2,3)
和(2,3,1)
来避免排列调用:
>>> sorted((1,2,3)) == sorted((2,3,1))
True
进入相同的顺序,然后比较排序的版本。
因此,为了将其扩展为is_computed
替换,文件从['F(1, 2, 3).pkl']
转为{(1, 2, 3):'F(1, 2, 3).pkl'}
,然后is_computed
变为:
files = { tuple(sorted(ast.literal_eval(name[1:].split('.')[0]))): name
for name in files }
def is_computed(files, signal):
name = tuple(sorted(s.fq for s in signal))
return name in files
答案 1 :(得分:1)
首先,如果你有很多对象,你可能需要考虑slots,namedtuple,pandas dataframes或numpy ndarray。这将大大降低每个项目的成本,删除每个对象的字典甚至每行对象元数据。
其次,从数组中删除项目是一项代价高昂的操作,涉及移动其后的所有项目。这适用于使用del或remove时的Python列表;更糟糕的是后者,它必须先找到项目,所以你正在读取整个数组,并为你删除的每个项目重写它的一部分。此时,最好构建一个包含您保留的项目的副本。另一种选择是用占位符替换不相关的项目,例如None,这是一种不需要移动其他条目的操作。
第三,根本不建立集合通常更有效。考虑一下:
def is_computed(files, s):
possibilities = list()
for elt in itertools.permutations(s):
possibilities.append(file_namer(s))
if any([name in files for name in possibilities]):
return True
else:
return False
在这段代码中,你构造了一个名为possible的(可能很大的)列表,通过在for循环中使用一个排列迭代器并为每个项调用file_namer来增长它(甚至不传递那个项!),然后构建另一个是否列出的列表每种可能性都已存在于文件中,最后将any()应用于该列表以获得结果。对于可能只需要检查一个答案的答案,这至少是两次通过。我不确定第一个循环是否需要存在,list comprehension当然应该是generator expression以允许any函数快捷方式。因此,假设file_namer等中没有隐藏任何副作用,我们可以将整个函数简化为:
def is_computed(files, s):
return file_namer(s) in files
但是如果file_namer(s)应该是file_namer(elt),正如我所料,它应该是:
def is_computed(files, s):
return any(file_namer(elt) in files
for elt in itertools.permutations(s))
另一个问题,因为我们正在考虑in
的重复files
测试,我们应该确保它是set
,dict
或其他类型的快membership tests。这将是Dan D建议排序而不是重复生成排列的点。例如,您可以拥有存储在字典中的实际对象的最低值(已排序)排列的索引。如果由于某种原因你不能使密钥可以使用,那么如果它们是可排序的,你可以使用binary searches。
这就是目前想到的。我没有仔细阅读。