是否可以提取包含重复值的交集列表?

问题描述 投票:1回答:5

我希望得到一个列表的交集,其中不会消除重复。我希望该方法是一种不使用循环的快速方法。以下是我的尝试,但此方法失败,因为删除了重复项。

a = ['a','b','c','f']
b = ['a','b','b','o','k']

tmp = list(set(a) & set(b))
>>>tmp
>>>['b','a']

我希望结果是['a', 'b', 'b']

在这种方法中,'a'是一个固定值,'b'是一个变量值。

以及从'a'中提取'b'值的概念。

有没有办法提取不删除重复值的交叉值列表?

python list set intersection
5个回答
2
投票

解决方案可能是

good = set(a)
result = [x for x in b if x in good]

这里有两个循环;一个是set的集合构建循环(在C中实现,比在Python中可以做的快一百倍)另一个是理解并在解释器中运行。第一个循环是为了避免在a中对b的每个元素进行线性搜索(如果a变大,这可能是一个严重的问题)。

请注意,使用filter可能不会获得太多(如果有的话),因为尽管filter循环在C中,但对于每个元素,它将不得不返回解释器来调用过滤函数。

请注意,如果你关心速度,那么Python可能不是一个好的选择...例如,PyPy可能会更好,在这种情况下,只是明确地编写一个最佳算法应该没问题(避免重新搜索a的重复项)在b中是连续的,就像在你的例子中发生的那样)

good = set(a)
res = []
i = 0
while i < len(b):
    x = b[i]
    if x in good:
        while i < len(b) and b[i] == x:  # is?
            res.append(x)
            i += 1
    else:
        i += 1

当然,在性能优化中,唯一真正的方法是在真实系统上尝试和测量真实数据......随着技术的进步和变得越来越复杂,猜测的工作越​​来越少。


1
投票

如果你坚持不明确使用for那么这将工作:

>>> list(filter(a.__contains__, b))
['a', 'b', 'b']

但是据我所知,直接调用像__contains__这样的魔术方法不是推荐的做法,所以请考虑一下:

>>> list(filter(lambda x: x in a, b))
['a', 'b', 'b']

如果你想改进a中从O(n)到O(1)的查找,那么先创建一个set

>>> a_set = set(a)
>>> list(filter(lambda x: x in a_set, b))
['a', 'b', 'b']

1
投票
>>a = ['a','b','c','f']
>>b = ['a','b','b','o','k']
>>items = set(a)
>>found = [i for i in b if i in items]
>>items
{'f', 'a', 'c', 'b'}
>>found
['a', 'b', 'b']

这应该做你的工作。


1
投票

我想它并不比循环快,最后你可能还需要一个循环来提取结果。无论如何...

from collections import Counter

a = ['a','a','b','c','f']
b = ['a','b','b','o','k']

count_b = Counter(b)
count_ab = Counter(set(b)-set(a))
count_b - count_ab

#=> Counter({'a': 1, 'b': 2})


I mean, if res holds the result, you need to:
[ val for sublist in [ [s] * n for s, n in res.items() ] for val in sublist ]
#=> ['a', 'b', 'b']

1
投票

目前尚不清楚在执行包含重复元素的列表交集时如何处理重复项,因为您只给出了一个测试用例及其预期结果,并且您没有解释重复处理。

根据目前保持重复的工作原理,常见的元素是'a''b',交叉列表列出了多重性为1的'a'和多重性为2的'b'。注意'a'在列表a和b上出现一次,但'b'在b上出现两次。交集列表列出具有多重性的公共元素,该公共元素等于具有最大多重性的该元素的列表。

答案是肯定的。但是,可以隐式调用循环 - 尽管您希望代码不显式使用任何循环语句。然而,该算法将始终是迭代的。

第1步:创建不包含重复项的交集Intersect(您已经完成了)。转换为列表以保持索引。

第2步:创建第二个数组IntersectD。创建一个新变量Freq,它使用count计算该公共元素的最大出现次数。使用IntersectFreq根据其对应的Intersect[k]多次附加元素Freq[k]

具有3个列表的示例代码将是

a = ['a','b','c','1','1','1','1','2','3','o']
b = ['a','b','b','o','1','o','1']
c = ['a','a','a','b','1','2']

intersect = list(set(a) & set(b) & set(c)) # 3-set case
intersectD = []

for k in range(len(intersect)):
  cmn = intersect[k]
  freq = max(a.count(cmn), b.count(cmn), c.count(cmn)) # 3-set case
  for i in range(freq): # Can be done with itertools
    intersectD.append(cmn)

>>> intersectD
>>> ['b', 'b', 'a', 'a', 'a', '1', '1', '1', '1']

对于涉及两个以上列表的情况,可以使用更复杂的集合交集和最大表达式来计算此公共元素的freq。如果使用列表列表,可以使用内部循环计算freq。您还可以使用How can I count the occurrences of a list item?中的itertools表达式替换内部i-loop。

© www.soinside.com 2019 - 2024. All rights reserved.