如何获取未包含在另一个列表中的n个元素?

时间:2015-06-27 15:46:34

标签: python

我有两个不同大小的列表(一个可以比另一个大),有一些共同的元素。我想从第一个列表中获取n个不在第二个列表中的元素。

我看到两个解决方案系列(以下示例适用于n=3

a = [i for i in range(2, 10)]
b = [i * 2 for i in range (1, 10)]
# [2, 3, 4, 5, 6, 7, 8, 9] [2, 4, 6, 8, 10, 12, 14, 16, 18]

# solution 1: generate the whole list, then slice
s1 = list(set(a) - set(b))
s2 = [i for i in a if i not in b]

for i in [s1, s2]:
    print (i[:3])

# solution 2: the simple loop solution
c = 0
s3 = []
for i in a:
    if i not in b:
        s3.append(i)
        c += 1
        if c == 3:
            break
print(s3)

所有这些都是正确的,输出是

[9, 3, 5]
[3, 5, 7]
[3, 5, 7]

(第一个解决方案没有给出第一个 3个,因为set不保留顺序 - 但在我的情况下这是可以的,因为我将没有排序(甚至明确洗牌)无论如何列出)

是否有最pythonic和最合理的?

解决方案1首先计算差异,然后是切片 - 我发现效率非常低(我的列表大小将是~100k元素,我将寻找前100个)。

解决方案2看起来更优,但它很丑陋(这是一个品味的问题,但我了解到,当Python中的某些东西看起来很丑陋时,它意味着通常有更多的pythonic解决方案)。

如果没有更好的选择,我会解决问题2。

3 个答案:

答案 0 :(得分:5)

我会使用set.difference和slice:

print(list(set(a).difference(b))[:3])
[3, 5, 7]

set.difference已经为你提供了不在b中的元素:

set([3, 5, 7, 9])

所以你只需要一点。

或者在集合中没有调用列表时使用iter,next和理解:

diff = iter(set(a).difference(b))
n = 3
sli = [next(diff) for _ in range(n)]
print(sli)

.difference不会创建第二个集合,因此它是一个更有效的解决方案:

In [1]: a = [i for i in range(2, 10000000)]  
In [2]: b = [i * 2 for i in range (1, 10000000)]   
In [3]: timeit set(a).difference(b)
1 loops, best of 3: 848 ms per loop    
In [4]: timeit set(a)- set(b)
1 loops, best of 3: 1.54 s per loop

对于s2 = [i for i in a if i not in b]以上的大型清单,会给你足够的时间在吃完之前做饭。

使用iter和.difference:

In [11]: %%timeit                                
diff = iter(set(a).difference(b))
n = 3
sli = [next(diff) for _ in range(n)]
   ....: 
1 loops, best of 3: 797 ms per loop

答案 1 :(得分:2)

如果您只需要100,那么避免构建完全差异可能会稍微快一些,但是依赖于您的数据集将会有多少。

>>> a = [random.randint(0, 10**6) for i in range(10**5)]
>>> b = [random.randint(0, 10**6) for i in range(10**5)]
>>> %timeit m1(a,b)
10 loops, best of 3: 121 ms per loop
>>> %timeit m2(a,b)
10 loops, best of 3: 98.7 ms per loop
>>> %timeit m3(a,b)
10 loops, best of 3: 82.3 ms per loop
>>> %timeit m4(a,b)
10 loops, best of 3: 42.8 ms per loop
>>> 
>>> a = list(range(10**5))
>>> b = [i*2 for i in a]
>>> %timeit m1(a,b)
10 loops, best of 3: 58.7 ms per loop
>>> %timeit m2(a,b)
10 loops, best of 3: 50.8 ms per loop
>>> %timeit m3(a,b)
10 loops, best of 3: 40.7 ms per loop
>>> %timeit m4(a,b)
10 loops, best of 3: 21.7 ms per loop

给了我

bset

通过更多工作,您甚至可以避免需要完整document.getElementById("container").appendChild(document.createTextNode('\ue145')) 。例如,如果你只查看列表的前10 ^ 4左右,你很可能会发现100个丢失,那么首先尝试它可能是值得的。但是,如果这在您的代码中成为瓶颈,我会感到惊讶,所以它可能不值得担心。

答案 2 :(得分:0)

可以将b转换为集合而不是a。设置一个生成器来利用懒惰,然后使用理解来获得你想要的项目:

a = [i for i in range(2, 10)]
b = [i * 2 for i in range (1, 10)]
bset = set(b)
agen = (i for i in a if not i in set(b))
first3 = [j for (i,j) in enumerate(agen) if i < 3]
print(first3)