【问题标题】:Set theory magic on list of tuples在元组列表上设置理论魔法
【发布时间】:2015-09-02 09:30:12
【问题描述】:

在 python 中,我有两个列表 AB。两个列表都包含元组(x,y)。例如:

A = [('x1','y1'), ('x2','y2'), ('x3','y3')]
B = [('x1','y1'), ('x2','y5'), ('x4','y4')]

现在,我想要三个结果。只要不涉及元组,所有这些都可以通过集合论轻松解决。

结果 1:两个列表的交集:set(A) & set(B))。所以结果应该是比较两个列表的元组的两个值。结果应该是:C = [('x1','y1')]

结果 2:只有 (x,y)[0] 匹配的两个列表的交集。结果应为:D = (('x1','y1'), ('x2', ('y2', 'y5'))]。理想情况下,解决方案是 D - C -> E = [('x2', ('y2', 'y5'))],但我可以接受 D 本身。

结果 3:列表 BA 相比的唯一性:set(B)-(set(A) & set(B))。仅在(x,y)[0] 上进行比较。结果应该是:[('x4', 'y4')]

我在这些问题上找不到任何东西,也无法自己构建解决方案。有人可以帮忙吗?

【问题讨论】:

  • 请注意,结果3不会是[(x2,y5), (x4,y4)]吗?
  • 你有什么尝试吗? tuple 是可散列的,因此您可以将 A / B 转换为集合并做您想做的事情。
  • @Mathias 你是绝对正确的。我更新了问题:)。
  • 您在A 末尾缺少)
  • @PM2Ring 已修复。还可以添加引号以立即复制粘贴到 python shell。

标签: python list set tuples set-theory


【解决方案1】:

这里有一些方法可以使用 dicts 做你想做的事。这是 Python 2 代码;它需要对 Python 3 进行一些小的修改。IIRC,Python 3 没有 dict.iteritems(),因为它的 dict.items() 返回一个迭代器而不是一个列表。

A = [('x1','y1'), ('x2','y2'), ('x3','y3')]
B = [('x1','y1'), ('x2','y5'), ('x4','y4')]

dA = dict(A)
dB = dict(B)

#Intersection, the simple way
print 'Result 1a:', list(set(A) & set(B))

#Intersection using dicts instead of sets
result = [(k, vA) for k, vA in dA.iteritems() if dB.get(k) == vA]
print 'Result 1b:', result

#match on 1st tuple element, ignoring 2nd element
result = {}
for k, vA in dA.iteritems():
    vB = dB.get(k)
    if vB:
        result[k] = (vA, vB) if vB != vA else vA
print 'Result 2a:', result.items()

#match on 1st tuple element only if 2nd elements don't match
result = {}
for k, vA in dA.iteritems():
    vB = dB.get(k)
    if vB and vB != vA:
        result[k] = (vA, vB)
print 'Result 2b:', result.items()

#unique elements of B, ignoring 2nd element
result = [(k, vB) for k, vB in dB.iteritems() if k not in dA]
print 'Result  3:', result

输出

Result 1a: [('x1', 'y1')]
Result 1b: [('x1', 'y1')]
Result 2a: [('x2', ('y2', 'y5')), ('x1', 'y1')]
Result 2b: [('x2', ('y2', 'y5'))]
Result  3: [('x4', 'y4')]

【讨论】:

  • 很好的答案,提供了所有可能的解决方案。非常感谢:)。
  • 请注意,将AB 转换为dicts 将丢弃共享相同x 坐标的点。尝试例如A = [('x1', 'y1'), ('x1', 'y2')].
  • @AndreaCorbellini:这是一个非常的好点。希望这对 Tim 的用例来说不是问题。
  • @Tim:您的列表会包含 Andrea 示例中的点吗?如果是这样,我的代码将需要一些修改。或者你可以只使用 Andrea 的代码。 :)
  • 我一直在做到底。毕竟逻辑:)。
【解决方案2】:

为什么不使用 python 的 set() ? 1 是非常直接的,2 是需要更多的工作:

A = [('x1','y1'), ('x2','y2'), ('x3','y3')]
B = [('x1','y1'), ('x2','y5'), ('x4','y4')]

a,b = set(A),set(B)
print '1:',a&b

axs = set(map(itemgetter(0),A))
bxs = set(map(itemgetter(0),B))

result2 = []
for c in axs&bxs:
    result2.append((c,set([y for x,y in A+B if x==c]))
print '2:',result2

输出:

1: set([('x1', 'y1')])
2: [('x2', set(['y2', 'y5'])), ('x1', set(['y1']))]

您可以对 3 使用非常相似的方法

【讨论】:

  • 这不适用于案例 3,因为 Tim 只想测试每个元组的第一个元素,如案例 2。
  • @Tim 没有注意到 3 也应该只依赖于 x 个匹配项,已更新
  • 虽然这个答案更 Pythonic,但为了完整性,我接受了另一个答案。
【解决方案3】:
  1. 两个列表的交集:

    您已经知道解决方案:set(A) & set(B)。或者,等效地,set(A).intersection(B)

    >>> A = [('x1', 'y1'), ('x2', 'y2'), ('x3', 'y3')]
    >>> B = [('x1', 'y1'), ('x2', 'y5'), ('x4', 'y4')]
    >>> set(A).intersection(B)
    {('x1', 'y1')}
    
  2. 只有 (x,y)[0] 匹配的两个列表的交集:

    首先,确保AB 都按它们的x 坐标排序。

    然后使用itertools.groupby() 和字典:

    >>> a_grouped = {x: list(points) for x, points in
    ...              itertools.groupby(A, lambda point: point[0])}
    >>> b_grouped = {x: list(points) for x, points in
    ...              itertools.groupby(B, lambda point: point[0])}
    >>> [(x, {point[1] for point in a_grouped[x] + b_grouped[x]})
    ...  for x in a_grouped if x in b_grouped]
    [('x2', {'y5', 'y2'}), ('x1', {'y1'})]
    

    (这和你问的不太一样,因为如你所见,我们有('x1', {'y1'})而不是('x1', 'y1')。此外,我们有集合而不是列表,但这些都是微不足道的修复。)

    如果您想排除共同点:在调用 groupby() 之前将它们从 AB 中删除:

    >>> A = set(A)
    >>> B = set(B)
    >>> common_points = A & B
    >>> A = [point for point in A if point not in common_points]
    >>> B = [point for point in B if point not in common_points]
    
  3. 列表BA比较的唯一性,仅在(x,y)[0]上比较:

    构造A中点的所有x坐标的集合:

    >>> exclude = {point[0] for point in A}
    >>> [point for point in B if point[0] not in exclude]
    [('x4', 'y4')]
    

    注意exclude的元素是a_grouped的键——这意味着你可以重用上一个问题的部分代码并编写:

    >>> [point for point in B if point[0] not in a_grouped]
    [('x4', 'y4')]
    

对于所有这些解决方案,性能和可读性都可以提高,如果您要使用我的代码,请考虑这一点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-31
    • 2011-11-21
    • 1970-01-01
    相关资源
    最近更新 更多