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

时间:2019-02-24 08:49:30

标签: python list set intersection

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

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'值的概念。

有没有一种方法可以提取不删除重复值的交叉值列表?

5 个答案:

答案 0 :(得分: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可能会更好,在这种情况下,只需明确编写最佳算法就可以了(避免重新搜索{{ 1}}(例如您的示例中出现的a中连续的重复项)

b

当然,在性能优化中,唯一的实际方法是尝试在实际系统上使用真实数据进行测量……随着技术的进步和变得越来越复杂,猜测的作用越来越小。

答案 1 :(得分: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']

这应该可以完成您的工作。

答案 2 :(得分:1)

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

根据当前保留重复项的工作方式,公用元素为'a''b',交集列表列出'a'的多重性为1,而'b'的多重性为2。注意'a'在两个列表 a b 上都出现一次,但是'b' b 上出现两次。相交列表列出了具有等于 maximum 多重性的元素的多重性的公共元素。

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

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

步骤2:创建另一个数组IntersectD。使用Freq创建一个新变量count,该变量计算该公共元素的最大出现次数。根据元素Intersect的使用次数,使用FreqIntersect[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']

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

答案 3 :(得分: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']

答案 4 :(得分: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})


我的意思是,如果res保留结果,则需要:

[ val for sublist in [ [s] * n for s, n in res.items() ] for val in sublist ]
#=> ['a', 'b', 'b']