如何唯一地组合2个列表

时间:2014-10-25 00:46:45

标签: python list

我正在处理非常长的列表,并试图想出一种迭代解决方案,以独特的方式组合这两个列表。

例如,我有列表

a = [TF1,Tar1]
b = [Tar1, TF1]

我想要包含元组的以下迭代器(如果可能):

(TF1,Tar1)    
(TF1,TF1)  
(Tar1,Tar1)  

这排除了(Tar1,TF1)因为已经添加了相反的顺序。

我目前的方法是遍历每个列表并使用字典来跟踪已添加的内容。这占用了大量的RAM,因为列表a长12,000,列表b长15000。使得到的字典包含大约* b / 2个条目,在这种情况下是90M条目。

任何建议都表示赞赏。感谢

4 个答案:

答案 0 :(得分:2)

基本上,问题出现在两个列表之间的共同元素上。如果您可以分离组合常见元素和唯一元素的情况,则可以解决您的问题

即。您需要创建以下笛卡尔产品

a_unique X b_unique
a_unique X b_common
a_common X b_unique
a_common X b_common 

在这四种情况中,最后一种情况会产生问题,因为它会创建非唯一对。再想一想,最后一个具有唯一对的笛卡尔坐标是a_common中2个元素的简单选择。

最后,隔离元素可以通过创建一个集合和两个列表然后迭代进行比较来完成

>>> #Sample Lists
>>> a = ['C0','C1','C2','A0','A1','A2']
>>> b = ['C0','C1','C2','B0','B1','B2']
>>> from itertools import product, combinations, chain
>>> # Create sets for O(1) lookup
>>> a_key = set(a)
>>> b_key = set(b)
>>> # Segerate elements to unique and common for both lists
>>> a = {'common':a_key & b_key,
         'unique':a_key - common}
>>> b = {'common':a_key & b_key,
         'unique':b_key - common}
>>> # Create cartesian products forall the cases
>>> list(chain.from_iterable([product(a['unique'], b['unique']),
                      product(a['unique'], b['common']),
                      product(a['common'], b['unique']),
                      combinations(a['common'], 2)]))
[('A0', 'B0'), ('A0', 'B1'), ('A0', 'B2'), ('A1', 'B0'), ('A1', 'B1'), ('A1', 'B2'), ('A2', 'B0'), ('A2', 'B1'), ('A2', 'B2'), ('A0', 'C0'), ('A0', 'C1'), ('A0', 'C2'), ('A1', 'C0'), ('A1', 'C1'), ('A1', 'C2'), ('A2', 'C0'), ('A2', 'C1'), ('A2', 'C2'), ('C0', 'B0'), ('C0', 'B1'), ('C0', 'B2'), ('C1', 'B0'), ('C1', 'B1'), ('C1', 'B2'), ('C2', 'B0'), ('C2', 'B1'), ('C2', 'B2'), ('C0', 'C1'), ('C0', 'C2'), ('C1', 'C2')]

答案 1 :(得分:1)

要迭代生成对,您需要查看itertools.product函数:

>>> l1 = [1, 2, 3]
>>> l2 = [1, 3, 7]
>>> import itertools
>>> list(itertools.product(l1, l2))
[(1, 1), (1, 3), (1, 7), (2, 1), (2, 3), (2, 7), (3, 1), (3, 3), (3, 7)]

但是,我不认为可以删除重复对而不跟踪你已经看过的那些。

要删除内存中的重复项,我会对元组进行排序并使其成为一组:

>>> pairs = list(itertools.product(l1, l2))
>>> set(map(tuple, map(sorted, pairs)))
set([(1, 2), (2, 7), (1, 3), (3, 3), (2, 3), (1, 7), (3, 7), (1, 1)])

如果你想保持低内存并且你可以使用磁盘,我建议使用类似于this approach的磁盘文件支持的合并排序。在遍历itertools.product的结果时,对该对进行排序并将其写入磁盘。然后使用合并排序并读取排序列表,删除重复项(因为它们将相邻)。

答案 2 :(得分:1)

我认为您可以避免重复,而不会存储所有到目前为止生成的值。相反,您希望检查以后生成哪些值将反向生成,并仅跟踪这些项目。如果你没有大量的碰撞,这将需要更少的内存(尽管在最坏的情况下它仍然是O(M*N)

我是这样做的:

import itertools

def product_without_reversed_duplicates(a, b):
    a_set = set(a)
    b_set = set(b)
    dupes = set()

    for x, y in itertools.product(a, b):
        if (x, y) not in dupes: # take (x, y) only if it is not a dupe of a previous item
            yield x, y
            if x in b_set and y in a_set:  # test if (y, x) will be generated later
                dupes.add((y, x))          # if so, add it to the set to be skipped

请注意,这假设ab没有任何内部重复,并且您希望尽可能保留产品的顺序(仅跳过反向对)。如果ab中的重复项可能存在,则您需要迭代itertools.product(a_set, b_set)而不是我上面的内容。然而,这将以任意顺序给出结果。您可以通过额外的步骤来解决这个问题,以便在保留订单的同时重复数据删除ab,但如果您需要,我会留给您找出相应的代码。

答案 3 :(得分:1)

相当棘手,但这是一种方法,O(n)额外的记忆。

xs = ['a', 'b', 'd']
ys = ['b', 'a', 'c']

def unique(seq):
    seen = set()
    seen_add = seen.add
    return [ x for x in seq if not (x in seen or seen_add(x))]

xs = unique(xs)
ys = unique(ys)

x_added = set()
for x in xs:
    for y in ys:
        if y in x_added and x in set(ys):
            continue
        print(x, y)
    x_added.add(x)

输出:

a b
a a
a c
b b
b c
d b
d a
d c

基本上,我们知道已经产生了一对,如果y已经出现在x s之一,x是其中一个ys因为我们已经为之前的y迭代了所有x s。独特的要求只是使处理特殊情况变得更容易。