找到2个python列表之间的“重叠”

时间:2011-02-23 16:54:25

标签: python list

给出2个列表:

a = [3,4,5,5,5,6]
b = [1,3,4,4,5,5,6,7]

我想找到“重叠”:

c = [3,4,5,5,6]

我也喜欢它,如果我可以提取“剩余”部分a和b不在c中。

a_remainder = [5,]
b_remainder = [1,4,7,]

注意: a有三个5,b有两个。 b里面有两个4,而且有一个。

结果列表c应该有两个5(由列表b限制)和一个4(由列表a限制)。

这给了我想要的东西,但我不禁想到有更好的方法。

import copy

a = [3,4,5,5,5,6]
b = [1,3,4,4,5,5,6,7]

c = []
for elem in copy.deepcopy(a):
    if elem in b:
        a.pop(a.index(elem))
        c.append(b.pop(b.index(elem)))

# now a and b both contain the "remainders" and c contains the "overlap"

另一方面,对于我所要求的,比“重叠”和“剩余”更准确的名称是什么?

8 个答案:

答案 0 :(得分:17)

Python 2.7中提供的

collection.Counter可用于实现完全符合您需要的多字符集。

a = [3,4,5,5,5,6]
b = [1,3,4,4,5,5,6,7]

a_multiset = collections.Counter(a)
b_multiset = collections.Counter(b)

overlap = list((a_multiset & b_multiset).elements())
a_remainder = list((a_multiset - b_multiset).elements())
b_remainder = list((b_multiset - a_multiset).elements())

print overlap, a_remainder, b_remainder

答案 1 :(得分:6)

在集合语言中,重叠是“交集”,余数是“设置差异”。如果你有不同的项目,你不必自己做这些操作,如果你有兴趣,请查看http://docs.python.org/library/sets.html

由于我们不使用不同的元素,因此您的方法是合理的。如果你想让它运行得更快,你可以为每个列表创建一个字典,并将数字映射到每个数组中有多少元素(例如,在a,3-> 1,4-> 1,5->中; 2等)。然后,您将迭代映射a,确定该字母是否存在,减少其计数并将其添加到新列表

未经测试的代码,但这是一个想法

def add_or_update(map,value):
    if value in map:
        map[value]+=1
    else
        map[value]=1

b_dict = dict()
for b_elem in b:
    add_or_update(b_dict,b_elem)

intersect = []; diff = [];

for a_elem in a:
    if a_elem in b_dict and b_dict[a_elem]>0:
        intersect.add(a_elem);

for k,v in diff:
    for i in range(v):
        diff.add(k);

答案 2 :(得分:4)

使用python set

intersection = set(a) & set(b)
a_remainder = set(a) - set(b)
b_remainder = set(b) - set(a)

答案 3 :(得分:4)

好的,详细的,但有点酷(与collections.Counter想法相似,但更多是自制的):

import itertools as it
flatten = it.chain.from_iterable 
sorted(
   v for u,v in 
    set(flatten(enumerate(g) 
        for k, g in it.groupby(a))).intersection(
    set(flatten(enumerate(g)
        for k, g in it.groupby(b))))
   )

基本思想是将每个列表放入一个新列表中,该列表将每个对象附加一个计数器,编号为重复项 - 这样您就可以对这些元组执行set操作了

稍微不那么冗长:

 aa = set(flatten(enumerate(g) for k, g in it.groupby(a)))
 bb = set(flatten(enumerate(g) for k, g in it.groupby(b)))
 # aa = set([(0, 3), (0, 4), (0, 5), (0, 6), (1, 5), (2, 5)])
 # bb = set([(0, 1), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (1, 4), (1, 5)])

 cc = aa.intersection(bb)
 # cc = set([(0, 3), (0, 4), (0, 5), (0, 6), (1, 5)])
 c = sorted(v for u,v in cc)
 # c = [3, 4, 5, 5, 6]
  • groupby - 生成包含相同元素的列表列表 (但由于语法需要g for k,g in it.groupby(a)来提取每个列表)
  • enumerate - 为每个子列表的每个元素附加一个计数器
  • flatten - 创建一个列表
  • set - 转换为集合
  • intersection - 找到共同元素
  • sorted(v for u,v in cc) - 摆脱计数器并对结果进行排序

最后,我不确定剩下的人是什么意思;它似乎应该是我的aa-ccbb-cc,但我不知道你在哪里a_remainder = [4]

sorted(v for u,v in aa-cc)
# [5]

sorted(v for u,v in bb-cc)
# [1, 4, 7]

答案 4 :(得分:2)

来自freenode的#python中kerio的回复:

[ i for i in itertools.chain.from_iterable([k] * v for k, v in \
  (Counter(a) & Counter(b)).iteritems())
]

答案 5 :(得分:1)

尝试difflib.SequenceMatcher(),“用于比较任何类型序列对的灵活类”......

快速尝试:

a = [3,4,5,5,5,6]
b = [1,3,4,4,5,5,6,7]

sm = difflib.SequenceMatcher(None, a, b)
c = []
a_remainder = []
b_remainder = []

for tag, i1, i2, j1, j2 in sm.get_opcodes():
    if tag == 'replace':
        a_remainder.extend(a[i1:i2])
        b_remainder.extend(b[j1:j2])
    elif tag == 'delete':
        a_remainder.extend(a[i1:i2])
    elif tag == 'insert':
        b_remainder.extend(b[j1:j2])
    elif tag == 'equal':
        c.extend(a[i1:i2])

现在......

>>> print c
[3, 4, 5, 5, 6]
>>> print a_remainder
[5]
>>> print b_remainder
[1, 4, 7]

答案 6 :(得分:-1)

Aset = Set(a);
Bset = Set(b);
a_remainder = a.difference(b);
b_remainder = b.difference(a);
c = a.intersection(b);

但如果您需要c有重复项,并且订单对您很重要, 你可能会寻找w:Longest common subsequence problem

答案 7 :(得分:-1)

我认为你不应该真正使用这个解决方案,但我借此机会练习lambda函数,这就是我想出的:)

a = [3,4,5,5,5,6]
b = [1,3,4,4,5,5,6,7]
dedup = lambda x: [set(x)] if len(set(x)) == len(x) else [set(x)] + dedup([x[i] for i in range(1, len(x)) if x[i] == x[i-1]])
default_set = lambda x: (set() if x[0] is None else x[0], set() if x[1] is None else x[1])
deduped = map(default_set, map(None, dedup(a), dedup(b)))
get_result = lambda f: reduce(lambda x, y: list(x) + list(y), map(lambda x: f(x[0], x[1]), deduped))
c = get_result(lambda x, y: x.intersection(y))          # [3, 4, 5, 6, 5]
a_remainder = get_result(lambda x, y: x.difference(y))  # [5]
b_remainder = get_result(lambda x, y: y.difference(x))  # [1, 7, 4]

我很确定izip_longest会简化这一点(不需要default_set lambda),但是我用Python 2.5进行了测试。

以下是计算中使用的一些中间值,以防有人想要理解这一点:

dedup(a) = [set([3, 4, 5, 6]), set([5]), set([5])]
dedup(b) = [set([1, 3, 4, 5, 6, 7]), set([4, 5])]
deduped = [(set([3, 4, 5, 6]), set([1, 3, 4, 5, 6, 7])), (set([5]), set([4, 5])), (set([5]), set([]))]