从列表中有效删除元素

时间:2018-05-07 09:50:54

标签: python list

我有一个巨大的对象列表:(大约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+个参数)。

2 个答案:

答案 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 filesfiles,则测试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)

首先,如果你有很多对象,你可能需要考虑slotsnamedtuplepandas dataframesnumpy 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测试,我们应该确保它是setdict或其他类型的快membership tests。这将是Dan D建议排序而不是重复生成排列的点。例如,您可以拥有存储在字典中的实际对象的最低值(已排序)排列的索引。如果由于某种原因你不能使密钥可以使用,那么如果它们是可排序的,你可以使用binary searches

这就是目前想到的。我没有仔细阅读。