【问题标题】:Group Python list of lists into groups based on overlapping items根据重叠项将列表的 Python 列表分组
【发布时间】:2013-04-24 17:38:40
【问题描述】:

我有一个列表列表,我正在尝试根据它们的项目对它们进行分组或聚类。如果没有元素在前一个组中,则嵌套列表会启动一个新组。

输入:

paths = [  
        ['D', 'B', 'A', 'H'],  
        ['D', 'B', 'A', 'C'],  
        ['H', 'A', 'C'],  
        ['E', 'G', 'I'],  
        ['F', 'G', 'I']]

我失败的代码:

paths = [
    ['D', 'B', 'A', 'H'],
    ['D', 'B', 'A', 'C'],
    ['H', 'A', 'C'],
    ['E', 'G', 'I'],
    ['F', 'G', 'I']
]
groups = []
paths_clone = paths
for path in paths:
    for node in path:
        for path_clone in paths_clone:
            if node in path_clone:
                if not path == path_clone:
                    groups.append([path, path_clone])
                else:
                    groups.append(path)
print groups

预期输出:

[
 [
  ['D', 'B', 'A', 'H'],
  ['D', 'B', 'A', 'C'],
  ['H', 'A', 'C']
 ],
 [
  ['E', 'G', 'I'],
  ['F', 'G', 'I']
 ]
]

另一个例子:

paths = [['shifter', 'barrel', 'barrel shifter'],
         ['ARM', 'barrel', 'barrel shifter'],
         ['IP power', 'IP', 'power'],
         ['ARM', 'barrel', 'shifter']]

预期输出组:

output = [
         [['shifter', 'barrel', 'barrel shifter'],
         ['ARM', 'barrel', 'barrel shifter'],
         ['ARM', 'barrel', 'shifter']],
         [['IP power', 'IP', 'power']],
         ]

【问题讨论】:

  • 组的定义是什么?而paths_clone 是对同一个列表的引用,而不是副本。使用paths[:] 创建副本。
  • @MartijnPieters 该组基本上是所有具有重叠项目的列表。如上所示,我需要一种方法来根据与其他列表相同的项目对列表进行聚类。
  • 所以当一个新组中的项目没有出现在前一个组中时,或者在上一个item中出现时,一个新组开始?
  • @MartijnPieters 是的!上一组

标签: python algorithm list


【解决方案1】:

您正在根据集合进行分组,因此请使用集合来检测新组:

def grouper(sequence):
    group, members = [], set()

    for item in sequence:
        if group and members.isdisjoint(item):
            # new group, yield and start new
            yield group
            group, members = [], set()
        group.append(item)
        members.update(item)

    yield group

这给出了:

>>> for group in grouper(paths):
...     print group
... 
[['D', 'B', 'A', 'H'], ['D', 'B', 'A', 'C'], ['H', 'A', 'C']]
[['E', 'G', 'I'], ['F', 'G', 'I']]

或者您可以再次将其转换为列表:

output = list(grouper(paths))

这假设组是连续的。如果您有不相交的组,则需要处理整个列表并遍历迄今为止为每个项目构建的所有组:

def grouper(sequence):
    result = []  # will hold (members, group) tuples

    for item in sequence:
        for members, group in result:
            if members.intersection(item):  # overlap
                members.update(item)
                group.append(item)
                break
        else:  # no group found, add new
            result.append((set(item), [item]))

    return [group for members, group in result]

【讨论】:

  • @Martjin 我没有得到相同的输出。我得到4组。我哪里错了。 [['D', 'B', 'A', 'H']] [['D', 'B', 'A', 'C']] [['H', 'A', 'C '], ['E', 'G', 'I']] [['F', 'G', 'I']]
  • @ArunprasathShankar:检查您是否具有相同的功能,一开始那里有错误(not 不应该出现)。
  • @Martjin 嗨,Martjin 在这种情况下失败了。路径 = [['shifter', 'barrel', 'barrel shifter'], ['ARM', 'barrel', 'barrel shifter'], ['IP power', 'IP', 'power'], [' ARM', 'barrel', 'shifter']] 我得到 3 组而不是两组 :( [['shifter', 'barrel', 'barrel shifter'], ['ARM', 'barrel', 'barrel shifter'] ']] [['IP 电源', 'IP', '电源']] [['ARM', '桶', '移位器']]
  • @ArunprasathShankar:那是因为你没有指定你有不相交的组。这需要先使用字典在内存中创建所有组。
  • @Martjin Pieters 您能否提供一些关于我将如何完成此任务的见解!
【解决方案2】:

与 Python 中大多数以“我正在尝试按 foo 分组...”开头的问题一样,答案是“使用 itertools.groupby 和 foo 作为键。”


首先,让我们采用一个非常简单的分组标准:每个列表的长度。为此,关键功能只是len。 (您可能还想先sort,可能使用相同的密钥,具体取决于您的数据。)

groups = [list(group) for key, group in itertools.groupby(paths, len)]

有时很难或不可能根据每个元素的独立变换来定义分组标准(以及因此的关键功能)。在这些情况下,groupby 通常不是答案(尽管groupby 加上另一个itertools 函数仍然可能是)。

在这种情况下,定义分组标准最自然的方法是与相邻元素进行比较。最简单的写法是写一个cmp函数,比较两个相邻元素,然后在上面使用functools.cmp_to_key

def cmp_paths(lhs, rhs):
    return 0 if any(key in lhs for key in rhs) else -1
key_paths = functools.cmp_to_key(cmp_paths)
groups = [list(group) for key, group in itertools.groupby(paths, key_paths)]

【讨论】:

  • defaultdict(list) 是分组事物的另一种选择(其中foo 是关键)。这样做的好处是无论输入是否排序,分组都是在 O(N) 操作中完成的。 (但它确实要求密钥是可散列的)。
  • @abamert 我不想根据列表的长度对列表进行分组。如上例所示,我想根据列表中的重叠项目对它们进行分组。
  • @ArunprasathShankar:“基于他们的项目”是什么意思?说明两个元素是否出现在同一组中的实际规则是什么?在任何人都可以用 Python 可以理解的方式解释之前,你需要先用人类可以理解的方式来解释它。
  • @abarnert 抱歉含糊不清。更正了评论。请检查一下
  • 我仍然不确定我是否理解这个标准……但听起来你可以很容易地为此编写一个cmp 函数,然后使用cmp_to_key 将其转换为一个键。所以让我编辑答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-22
  • 2019-02-02
  • 2020-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多