首页 > 解决方案 > Python:如何获取仅出现在一组列表中的项目?

问题描述

我想创建一个函数,该函数采用一个或多个集合的列表并找到列表中所有集合的对称差异,即结果应该是一组值,每个值只包含在一个个体中套。(如果我错认为这是对称差异,请纠正我。)

例如:

>>> s1 = set([1, 2, 3])
>>> s2 = set([2, 3, 4])
>>> s3 = set([2, 3, 7])
>>> s4 = set([2, 5, 9])
>>> myfunc([s1, s2, s3, s4])
{1, 4, 5, 7, 9}

有什么内置的东西可以用来代替上面的myfunc吗?还是我使用这样的东西:

def myfunc(sets: List[set]) -> set:

    sd = set()
    goners = set()
    for s in sets:
        still_ok = s - goners
        sd = sd.symmetric_difference(still_ok)
        goners = goners.union(s.difference(sd))
    return sd

有没有更好/更有效/“Pythonic”的方法来做到这一点?

标签: pythonset

解决方案


那这个呢:

from collections import Counter

s1 = set([1, 2, 3])
s2 = set([2, 3, 4])
s3 = set([2, 3, 7])
s4 = set([2, 5, 9])
print([k for k,v in Counter((*s1,*s2,*s3,*s4)).items() if v == 1])

Eventho 这看起来不错,因为它是一个单线器,你必须记住它比你自己的方法慢一点:

In [85]: def nicefunc(sets): 
    ...:     return [k for k,v in Counter(itertools.chain.from_iterable(sets)).items() if v == 1] 
    ...:                                                                                                                                                                                       

In [86]: def nicefunc2(sets): 
    ...:     return [k for k,v in Counter( [i for s in sets for i in s]).items() if v == 1] 
    ...:                                                                                                                                                                                       

In [87]: def nicefunc3(): 
    ...:     return [k for k,v in Counter((*s1,*s2,*s3,*s4)).items() if v == 1] 
    ...:                                                                                                                                                                                       

In [88]: def myfunc(sets): 
    ...:     sd = set() 
    ...:     goners = set() 
    ...:     for s in sets: 
    ...:         still_ok = s - goners 
    ...:         sd = sd.symmetric_difference(still_ok) 
    ...:         goners = goners.union(s.difference(sd)) 
    ...:     return sd 
    ...:                                                                                                                                                                                       

In [89]: sets = [s1, s2, s3, s4]                                                                                                                                                               

In [90]: %timeit myfunc(sets)                                                                                                                                                                  
2.25 µs ± 2.53 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [91]: %timeit nicefunc(sets)                                                                                                                                                                
3.64 µs ± 23 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [92]: %timeit nicefunc2(sets)                                                                                                                                                               
3.79 µs ± 11.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [94]: %timeit nicefunc3()                                                                                                                                                                   
3.64 µs ± 18.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

您还可以选择另一种方法,它仍然是单行但速度更快:

In [152]: def coolfunc(sets): 
     ...:     return set.union(*[sets[i]-set.union(*sets[:i],*sets[i+1:]) for i in range(len(sets))]) 

In [153]: coolfunc(sets)                                                                                                                                                                       
Out[153]: {1, 4, 5, 7, 9}

In [154]: %timeit coolfunc(sets)                                                                                                                                                               
3.34 µs ± 19.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

但是,正如@VBrail 所指出的,您对集合集合的对称集合差异的定义是错误的。这是一个用于计算集合的实际对称集差异的单行方法,定义为

集合的对称差异仅包含集合维基百科中奇数个集合中的元素

from functools import reduce                                                                                                                                                          
s1 = set([1, 2, 3]) 
s2 = set([2, 3, 4]) 
s3 = set([2, 3, 7]) 
s4 = set([2, 5, 9])                                                                                                                                                                   
sets = [s1,s2,s3,s4]                                                                                                                                                                  
reduce(set.symmetric_difference, sets)      

{1、3、4、5、7、9}


推荐阅读