易学;在Python中获得多个元组列表的第二个元素的交集的有效方法?

易学;在Python中获得多个元组列表的第二个元素的交集的有效方法?,python,python-2.7,Python,Python 2.7,我是Python新手(在2.7中工作),我发现这是一个非常有价值的资源 比如说,我正在处理几个2元素元组的列表,这些元组的形式通常为(ID,value),例如 我真正想做的是找到一种简单(且计算效率高)的方法来获得这些元组的第二个元素的交集。我查了一下,发现布景可能会做我想做的。。。并帮助我理解如何获得两个列表的交集 我知道我可以通过这样循环元组来创建三个全新的“仅值”列表: newList1 = [] for tuple in list1: newList1.append(tuple[1

我是Python新手(在2.7中工作),我发现这是一个非常有价值的资源

比如说,我正在处理几个2元素元组的列表,这些元组的形式通常为(ID,value),例如

我真正想做的是找到一种简单(且计算效率高)的方法来获得这些元组的第二个元素的交集。我查了一下,发现布景可能会做我想做的。。。并帮助我理解如何获得两个列表的交集

我知道我可以通过这样循环元组来创建三个全新的“仅值”列表:

newList1 = []
for tuple in list1:
   newList1.append(tuple[1])
newList2 = []
for tuple in list2:
   newList2.append(tuple[1])
newList3 = []
for tuple in list3:
   newList3.append(tuple[1])
然后得到每一对的交点,如下所示:

i_of_1and2 = set(newList1).intersection(newList2)
i_of_1and3 = set(newList2).intersection(newList3)
i_of_2and3 = set(newList1).intersection(newList3)
但是我的列表有点大——就像数十万(有时是几千万)元组。这真的是获得这三个列表元组中第二个元素交集的最佳方法吗?对我来说,这似乎…不雅


谢谢你的帮助

您可以利用这样一个事实,即该方法需要两个或多个集合并找到它们的交点。此外,还可以使用来减少代码膨胀。最后,你可以把它做成一行。例如:

>>> list1 = [(111, 222), (111, 333), (111, 444)]
>>> list2 = [(555, 333), (555, 444), (555, 777)]
>>> list3 = [(123, 444), (123, 888), (123, 999)]
>>>
>>> set.intersection(*[set(t[1] for t in l) for l in (list1, list2, list3)])
set([444])
为了帮助您了解发生了什么,对
set.intersection(…)
的调用相当于以下python代码:

>>> allsets = []
>>> for l in (list1, list2, list3):
...   n = set()
...   for t in l:
...     n.add(t[1])
...   allsets.append(n)
... 
>>> allsets
[set([444, 333, 222]), set([777, 444, 333]), set([888, 444, 999])]
>>> allsets[0].intersection(allsets[1]).intersection(allsets[2])
set([444])

variable1
开始,您就遇到了一个大问题。通常情况下,variable1是一个不好的符号-如果您想要有多个值,请使用数据结构,而不是使用许多带有编号名称的变量。这可以阻止您一遍又一遍地重复代码,并有助于阻止bug

让我们改用列表列表:

values = [
    [(111, 222), (111, 333), (111, 444)],
    [(555, 333), (555, 444), (555, 777)],
    [(123, 444), (123, 888), (123, 999)]
]
现在我们只想得到子列表中每个元组的第二个元素。这很容易使用以下公式进行计算:

然后,我们需要两个项目之间的交点,我们用它来得到两个可能的不同对:

>>> for values, more_values in itertools.combinations(new_values, 2):
...     set(values).intersection(more_values)
... 
{444, 333}
{444}
{444}
所以,如果我们把它放在一起:

import itertools

values = [
    [(111, 222), (111, 333), (111, 444)],
    [(555, 333), (555, 444), (555, 777)],
    [(123, 444), (123, 888), (123, 999)]
]

sets_of_first_items = ({item[1] for item in sublist} for sublist in values)
for values, more_values in itertools.combinations(sets_of_first_items, 2):
    print(values.intersection(more_values))
这给了我们:

{444, 333}
{444}
{444}
Intersection of 0 and 1: {444, 333}
Intersection of 0 and 2: {444}
Intersection of 1 and 2: {444}
我在这里所做的更改是将内部列表设置为集合理解,避免创建一个列表只是为了将其转换为集合,并使用生成器表达式而不是列表理解,因为它是惰性计算的

最后请注意,如果您需要我们用于生成交叉点的列表的索引,可以简单地使用:

这给了我们:

{444, 333}
{444}
{444}
Intersection of 0 and 1: {444, 333}
Intersection of 0 and 2: {444}
Intersection of 1 and 2: {444}
编辑:

同样,这也是一个可以通过使用更好的数据结构得到极大帮助的问题。这里最好的选择是将用户id的dict用于一组产品id。当您只需要一个集合,并打算稍后将其转换为集合时,没有理由将数据存储为列表,而dict对于您尝试存储的数据类型来说是一个更好的解决方案

请参见以下示例:

import itertools

values = {
    "111": {222, 333, 444},
    "555": {333, 444, 777},
    "123": {444, 888, 999}
}

for (first_user, first_values), (second_user, second_values) in itertools.combinations(values.items(), 2):
    print("Intersection of {0} and {1}: {2}".format(first_user, second_user, first_values.intersection(second_values)))
给我们:

Intersection of 555 and 123: {444}
Intersection of 555 and 111: {444, 333}
Intersection of 123 and 111: {444}

我不确定您是否已经阅读过python中的词典,但这似乎符合您试图结合列表做得更好的内容。字典是由键和值组成的,就像用2元素元组模拟的一样

例如,list1、list2和list3可以表示为如下所示的字典(假设111是id): 你的字典={“111”:[222333444],“555”:[33344777],“123”:[44488999]}

因此,如果您想获取特定id的所有值,如“111”,您可以编写: 你的口述得到(“111”) 这将返回列表。这里还有一些字典文档的链接。
这里有一个简单的方法

>>> list1 = [(111, 222), (111, 333), (111, 444)]
>>> list2 = [(555, 333), (555, 444), (555, 777)]
>>> list3 = [(123, 444), (123, 888), (123, 999)]
>>> lists = [list1, list2, list3]
>>> set.intersection(*(set(zip(*list)[1]) for list in lists))
set([444])
  • 这个技巧用于解压元组并获得集合 第二要素
  • 用于将它们全部相交
  • 至于效率,我会先尝试简单的方法,然后看看
    在尝试优化之前,如果这足够快。

    通常最好给出示例输出-您期望得到的结果。这些列表是否按每个元组的第二个元素排序?是否允许对这些列表中的元素重新排序?如果是这样的话,您可以尝试一种类似合并排序的算法,避免复制数据。这一点很好。我希望输出是一个列表或集合,包含原始列表1和列表2中第二个元素的交集…即(ID,value)元组中的公共值。所以,对于i_of_1和2,我期望[333444]。啊,它们不是按第二个元素排序的。第一个元素是个人ID号,第二个元素是产品代码。我想知道常见的产品。+1和我做的一样:D唯一的一点是OP已经指定了Python 2.7,所以您的最后一个示例不会是交叉兼容的。太棒了!我来试试,谢谢。同时也感谢您为我们提供的关于编号名称的学习时刻。@jamylak:yikes,我会遇到兼容性问题吗?@CJH在我的最后一个示例中,只需从打印行中删除括号-在2.x中,打印是一个语句,而不是一个函数。编辑:更新到2.x和3.x版本。@Lattyware抱歉,我的错,它可以在Python2.7中工作,但不能更早。啊,看起来我也会使用它。谢谢我要指出的是,说你会使用
    你的dict.get(“111”)
    ,真是一件很奇怪的事。在Python中,99.9%的时间你会使用
    你的dict[“111”]
    >>> list1 = [(111, 222), (111, 333), (111, 444)]
    >>> list2 = [(555, 333), (555, 444), (555, 777)]
    >>> list3 = [(123, 444), (123, 888), (123, 999)]
    >>> lists = [list1, list2, list3]
    >>> set.intersection(*(set(zip(*list)[1]) for list in lists))
    set([444])