根据重叠项将Python列表分组到组中

时间:2013-04-30 17:52:20

标签: python algorithm list

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

输入:

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']],
         ]

3 个答案:

答案 0 :(得分:4)

与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)]

答案 1 :(得分:4)

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

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]

答案 2 :(得分:-1)

只需使用任何现有的群集算法即可。说K-Means或Hierarchical?

http://en.wikipedia.org/wiki/K-means_clustering

http://en.wikipedia.org/wiki/Hierarchical_clustering