【问题标题】:Group tuples inside a list by matching positions of two of its sub-elements通过匹配两个子元素的位置来对列表中的元组进行分组
【发布时间】:2018-07-05 07:39:19
【问题描述】:

我有一个如下的元组列表。元组本身就是一个嵌套元组,里面有 3 个子元素(元组)。

[(('a', 'apple'), ('b', 'mango'), ('c', 'grapes')),
 (('a', 'apple'), ('b', 'mango'), ('c', 'grapes')),
 (('e', 'apple'), ('b', 'mango'), ('c', 'grapes')),
 (('a', 'apple'), ('d', 'mango'), ('c', 'peach')),
 (('e', 'apple'), ('d', 'mango'), ('f', 'grapes')),
 (('f', 'grapes'), ('e', 'apple'), ('d', 'mango')),
 (('f', 'peach'), ('e', 'apple'), ('e', 'mango')),
 (('f', 'grapes'), ('c', 'apple'), ('d', 'mango')), 
 (('e', 'apple'), ('f', 'grapes'), ('d', 'mango')),
 (('a', 'apple'), ('c', 'grapes'), ('b', 'mango')),
 ]

我想通过匹配其中两个元素的位置来对这些元组进行分组,即。元组中的苹果和芒果(预先确定并已知)!

期望的输出:

[
# apple and mango at positions 1 and 2.
[(('a', 'apple'), ('b', 'mango'), ('c', 'grapes')),
 (('a', 'apple'), ('b', 'mango'), ('c', 'grapes')),
 (('e', 'apple'), ('b', 'mango'), ('c', 'grapes')),
 (('a', 'apple'), ('d', 'mango'), ('c', 'peach')),
 (('e', 'apple'), ('d', 'mango'), ('f', 'grapes'))],

# apple and mango at positions 2 and 3.
 [(('f', 'grapes'), ('e', 'apple'), ('d', 'mango')),
 (('f', 'peach'), ('e', 'apple'), ('e', 'mango')),
 (('f', 'grapes'), ('c', 'apple'), ('d', 'mango'))], 

# apple and mango at positions 1 and 3.
 [(('e', 'apple'), ('f', 'grapes'), ('d', 'mango')),
 (('a', 'apple'), ('c', 'grapes'), ('b', 'mango'))]
 ]

我尝试使用Counter 并检查了其他一些示例,但无法成功关闭所需的输出。因此,任何帮助或指点将不胜感激。

【问题讨论】:

  • 如果你有两个元组,其中applemango(('a', 'apple'), ('m', 'mango'))(('a', 'apple'), ('m', 'mango'))(('m', 'mango'), ('a', 'apple')) 这样交换,这两个应该组合在一起吗?
  • 那么它们应该放在不同的组中,因为 apple 和 mango 在第一个元组中的位置是 1 和 2,而在第二个元组中是 2 和 1 !

标签: python list group-by grouping itertools


【解决方案1】:

我对这样的任务进行分组的首选解决方案是collections.defaultdict。我已经写了一个关于分组的冗长答案,你可以阅读here。从该答案中挑选出相关的 sn-ps 为我们提供了这段代码:

import collections

groupdict = collections.defaultdict(list)
for value in your_list_of_tuples:  # input
    group = ???  # group identifier
    groupdict[group].append(value)

result = list(groupdict.values())  # output

剩下的就是找到一种方法,用一个可哈希的值唯一地表示每个组(也就是说,我们需要填写group = ??? 行)。

最简单的解决方案可能是从嵌套元组中提取applemango 值,并将所有其他值替换为None

>>> tup = (('a', 'apple'), ('c', 'grapes'), ('b', 'mango'))
>>> tuple((t[1] if t[1] in {'apple','mango'} else None) for t in tup)
('apple', None, 'mango')

添加它,我们就完成了:

import collections

groupdict = collections.defaultdict(list)
for value in your_list_of_tuples:
    group = tuple((t[1] if t[1] in {'apple','mango'} else None) for t in value)
    groupdict[group].append(value)

result = list(groupdict.values())

# result:
# [[(('a', 'apple'), ('b', 'mango'), ('c', 'grapes')),
#   (('a', 'apple'), ('b', 'mango'), ('c', 'grapes')),
#   (('e', 'apple'), ('b', 'mango'), ('c', 'grapes')),
#   (('a', 'apple'), ('d', 'mango'), ('c', 'peach')),
#   (('e', 'apple'), ('d', 'mango'), ('f', 'grapes'))],
#  [(('f', 'grapes'), ('e', 'apple'), ('d', 'mango')),
#   (('f', 'peach'), ('e', 'apple'), ('e', 'mango')),
#   (('f', 'grapes'), ('c', 'apple'), ('d', 'mango'))],
#  [(('e', 'apple'), ('f', 'grapes'), ('d', 'mango')),
#   (('a', 'apple'),('c', 'grapes'), ('b', 'mango'))]]

【讨论】:

  • 非常感谢您对您的方法和解决方案的详细解释。作为示例,它确实对我有所帮助,我强调的是一个基本示例,以便任何想要帮助的人都能理解它。但是,您的方法帮助我调整了“其他部分”,以便在这个嵌套元组列表中的其他元组元素上进行高级分组,如下面的示例! group = tuple((t[1] if t[1] in {'apple','mango'} else t[0]) for t in value) 最后,我重视并感谢您的知识共享能力更大的 Python 开发者社区! :)
【解决方案2】:

另一种方法是使用每对子项的位置作为键来制作字典。

给定

import more_itertools as mit

iterables = [
     (("a", "apple"),  ("b", "mango"),  ("c", "grapes")),
     (("a", "apple"),  ("b", "mango"),  ("c", "grapes")),
     (("e", "apple"),  ("b", "mango"),  ("c", "grapes")),
     (("a", "apple"),  ("d", "mango"),  ("c", "peach")),
     (("e", "apple"),  ("d", "mango"),  ("f", "grapes")),
     (("f", "grapes"), ("e", "apple"),  ("d", "mango")),
     (("f", "peach"),  ("e", "apple"),  ("e", "mango")),
     (("f", "grapes"), ("c", "apple"),  ("d", "mango")), 
     (("e", "apple"),  ("f", "grapes"), ("d", "mango")),
     (("a", "apple"),  ("c", "grapes"), ("b", "mango")),
]

whitelisted = "apple mango".split()

代码

首先,我们为locateiterables 中出现whitelisted 子项建立一个索引列表。

pred = lambda x: x[1] in set(whitelisted)
indices = [tuple(mit.locate(t, pred=pred)) for t in iterables]
print(indices)
# [(0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (1, 2), (1, 2), (1, 2), (0, 2), (0, 2)]

最后,map_reduce 是使用自定义键和值创建 defaultdict 的一种方法。

result = mit.map_reduce(zip(indices, iterable), keyfunc=lambda x: x[0], valuefunc=lambda x: x[1])
result

输出

defaultdict(None,
            {(0, 1): [(('a', 'apple'), ('b', 'mango'), ('c', 'grapes')),
              (('a', 'apple'), ('b', 'mango'), ('c', 'grapes')),
              (('e', 'apple'), ('b', 'mango'), ('c', 'grapes')),
              (('a', 'apple'), ('d', 'mango'), ('c', 'peach')),
              (('e', 'apple'), ('d', 'mango'), ('f', 'grapes'))],
             (1, 2): [(('f', 'grapes'), ('e', 'apple'), ('d', 'mango')),
              (('f', 'peach'), ('e', 'apple'), ('e', 'mango')),
              (('f', 'grapes'), ('c', 'apple'), ('d', 'mango'))],
             (0, 2): [(('e', 'apple'), ('f', 'grapes'), ('d', 'mango')),
              (('a', 'apple'), ('c', 'grapes'), ('b', 'mango'))]})

详情

对于iterables 中的每个元组,locate 用于生成属于whitelisted 项目集的项目的索引。这些结果足以将项目组合在一起。但是,更容易看到实际返回的项目,所以接下来我们用map_reduce 构建一个字典。

我们迭代 (indices, iterables) 对的 zip。 keyfuncindices 转换为键。同样,valuefunciterables 转换为值。结果是一个defaultdict,其值按whitelistedapplemango 的子位置分组。

【讨论】:

  • 谢谢! :) 这也是解决问题的一种有趣方式,尽管我已经使用了@aran 提到的解决方案,因为它使我能够调整其他元组元素以进行高级分组。
  • 没关系。请注意,对最终结果的任何调整都可能通过map_reduce 的关键字参数来完成,即keyfuncvaluefuncreducefunc.
  • 是的,当然! Map_reduce 是另一种选择。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-02-02
  • 2017-01-28
  • 2018-04-15
  • 2015-02-05
  • 2017-05-29
  • 1970-01-01
  • 2015-05-03
相关资源
最近更新 更多