Python:在大量列表中快速提取所有可能的2组合之间的交叉点

时间:2009-11-18 17:27:54

标签: python list set intersection combinations

我有一个ca.的数据集。 9K可变长度列表(1到100K元素)。我需要计算此数据集中所有可能的2列表组合的交集长度。请注意,每个列表中的元素都是唯一的,因此可以将它们存储为python中的集合。

在python中执行此操作的最有效方法是什么?

编辑我忘了指定我需要能够将交叉点值与相应的列表对匹配。感谢大家的迅速回应和对混乱的道歉!

3 个答案:

答案 0 :(得分:2)

当你需要产生一个(N乘N / 2)结果矩阵,即O(N平方)输出时,任何语言当然都不能小于O(N平方)。 (你的问题中N是“约9K”)。因此,我认为没有什么本质上比(a)使你需要的N组更快,以及(b)迭代它们以产生输出 - 即最简单的方法。 IOW:

def lotsofintersections(manylists):
  manysets = [set(x) for x in manylists]
  moresets = list(manysets)
  for  s in reversed(manysets):
    moresets.pop()
    for z in moresets:
      yield s & z

此代码已经尝试添加一些小的优化(例如,通过避免切片或弹出列表的前面,这可能会添加其他O(N平方)因子)。

如果您有许多可用的核心和/或节点,并且正在寻找并行算法,那么当然情况就不同了 - 如果您的情况如此,您能否提及您拥有的集群类型,其大小,节点和核心的方式最好的沟通,等等?

编辑:因为OP在评论(!)中随便提到他们实际上需要相交的集合的数量(实际上,为什么省略规范的这些关键部分?!至少编辑问题以澄清它们......),这只需要将其更改为:

  L = len(manysets)
  for i, s in enumerate(reversed(manysets)):
    moresets.pop()
    for j, z in enumerate(moresets):
      yield L - i, j + 1, s & z

(如果你需要从1开始计算渐进式标识符 - 否则会有明显的变化)。

但如果这是规范的一部分你也可以使用更简单的代码 - 忘记更多的设置,并且:

  L = len(manysets)
  for i xrange(L):
    s = manysets[i]
    for j in range(i+1, L):
      yield i, j, s & manysets[z]

这次假设您想要“从0开始计数”,只是为了变化; - )

答案 1 :(得分:2)

如果您的集合存储在s中,例如:

s = [set([1, 2]), set([1, 3]), set([1, 2, 3]), set([2, 4])]

然后你可以使用itertools.combinations将它们逐两取出来计算交集(请注意,正如Alex指出的那样,combinations仅在2.6版本之后可用)。这里有一个列表理解(仅为了示例):

from itertools import combinations
[ i[0] & i[1] for i in combinations(s,2) ]

或者,在一个循环中,这可能是你需要的:

for i in combinations(s, 2):
    inter = i[0] & i[1]
    # processes the intersection set result "inter"

因此,为了获得每一个的长度,“处理”将是:

    l = len(inter)

这非常有效,因为它使用迭代器来计算每个组合,并且不会提前准备所有组合。


编辑:请注意,使用此方法,列表“s”中的每个集实际上都可以是返回集合的其他内容,就像生成器一样。如果内存不足,列表本身可能只是一个生成器。它可能会慢很多,这取决于你如何生成这些元素,但你不需要同时在内存中拥有整个集合列表(不是在你的情况下它应该是一个问题)。

例如,如果每个集都来自函数gen

def gen(parameter):
    while more_sets():
        # ... some code to generate the next set 'x'
        yield x

with open("results", "wt") as f_results:
    for i in combinations(gen("data"), 2):
        inter = i[0] & i[1]
        f_results.write("%d\n" % len(inter))

编辑2 :如何收集索引(在redrat的评论之后)。

除了我在评论中回答的快速解决方案之外,收集设置索引的更有效方法是列出(index, set)而不是set列表。

新格式示例:

s = [(0, set([1, 2])), (1, set([1, 3])), (2, set([1, 2, 3]))]

如果您正在构建此列表以计算组合,则应该很容易适应您的新要求。主循环变为:

with open("results", "wt") as f_results:
    for i in combinations(s, 2):
        inter = i[0][1] & i[1][1]
        f_results.write("length of %d & %d: %d\n" % (i[0][0],i[1][0],len(inter))

在循环中,i[0]i[1]将是元组(index, set),因此i[0][1]是第一个集合,i[0][0]是其索引。

答案 2 :(得分:0)

试试这个:

_lists = [[1, 2, 3, 7], [1, 3], [1, 2, 3], [1, 3, 4, 7]]
_sets = map( set, _lists )
_intersection = reduce( set.intersection, _sets )

获取索引:

_idxs = [ map(_i.index, _intersection ) for _i in _lists ]

干杯,

JoséMaríaGarcía

PS:对不起我误解了这个问题

相关问题