【问题标题】:Find and remove matches from two lists in Python从 Python 中的两个列表中查找和删除匹配项
【发布时间】:2017-07-25 14:50:06
【问题描述】:

所以我有两个 int 列表 xs 和 ys(任意长度),并希望有效地删除每个列表中出现的重复项。由于您不能/不应该在循环遍历列表时对其进行编辑,因此我的尝试是:

matches = [match for match in xs if match in ys]
for match in matches: 
    xs.remove(match)
    ys.remove(match)

但重要的是,如果该值在 xs 中出现两次但在 ys 中出现一次,则会产生错误,因为“如果在 ys 中匹配”对于相同的值计算为 true 两次,但我需要它只计算一次。

所以澄清一下,如果:

xs = [0, 2, 4, 8, 8, 100]
ys = [1, 3, 5, 8, 8, 8, 10]

那么 8 需要从两个列表中删除两次。

我怎样才能有效地做到这一点?谢谢

edit:列表不一定是有序的,实际上两个列表都可能包含重复项。

【问题讨论】:

    标签: python list-comprehension


    【解决方案1】:

    您可以对交叉点使用多重集 (collections.Counter)。如果您预计会有大量重复并且您的列表很大,那么建议避免使用.remove,因为它很昂贵。列表理解的扩展性要好得多。我们使用一些适度的 itertools 技巧来过滤掉标记的重复项。请注意,这可以进行调整,例如不删除第一次出现,而是删除最后一次出现等。

    from collections import Counter
    from itertools import repeat, chain
    
    xc = Counter(xs)
    yc = Counter(ys)
    matches = xc & yc
    tr = repeat(True)
    rm = {k: chain(repeat(False, m), tr) for k, m in matches.items()}
    xs = [x for x in xs if not x in rm or next(rm[x])]
    rm = {k: chain(repeat(False, m), tr) for k, m in matches.items()}
    ys = [y for y in ys if not y in rm or next(rm[y])]
    

    请注意,这只适用于您的元素是可散列的。

    【讨论】:

    • 它仅适用于 2 个重复项,但不适用于 n 个重复项,已更新示例以显示这一点
    • @BenJones 啊,我明白了。你能澄清一下吗?在这种情况下应该发生什么:xs=[1,1,1,1,2]ys=[1,1,3]?想要的输出? xs=[1,1,1,2]ys=[1,3]?或xs=[1,1,2]ys=[3]xs=[1,2]ys=[3]?
    • 两个列表共有两个 1,因此所需的输出是 xs = [1,1,2] 和 ys = [3],很抱歉造成混淆!
    • @BenJones 对不起,我自己对那些 Counter 对象不熟悉。他们实现了类似多重集的东西,像一个集但具有多重性。例如,将它们相交会使每个元素在两者中出现的次数更少。它有这个elements() 迭代器,它对删除循环很有用,因为它产生每个元素的频率与它在多重集中的频率一样高。我认为它现在应该可以正常工作了。
    • @BenJones 我已经摆脱了remove 位,并且根据重复的数量,现在对于大型(100,000 个元素)列表来说速度要快得多(x100+)。
    猜你喜欢
    • 1970-01-01
    • 2017-05-20
    • 1970-01-01
    • 2012-07-21
    • 1970-01-01
    • 2018-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多