将列表中的元素收集到相等元素列表的列表中?

时间:2015-09-07 20:25:47

标签: python

我有一个python中的对象列表,以及一个比较函数,给定两个对象,决定它们是否应该被认为是相等的。

我想将对象列表转换为新的列表列表,其中每个子列表都会收集比较相等的元素。

这是一种pythonic方式吗?

2 个答案:

答案 0 :(得分:2)

拥有compare函数,根据元素是否相等而返回TrueFalse并不理想。基本上,您必须将每个元素与每个现有组中的一些“原型”进行比较。像这样:

def group(elements, comp_func):
    groups = {}
    for x in elements:
        for y in groups:
            if comp_func(x, y):
                groups[y].append(x)
                break
        else:
            groups[x] = [x]
    return groups

或者更短(但不是更快):

def group(elements, comp_func):
    groups = {}
    for x in elements:
        prototype = next((y for y in groups if comp_func(x, y)), x)
        groups.setdefault(prototype, []).append(x)
    return groups

示例:

>>> def comp_len(o1, o2):
...     return len(o1) == len(o2)
>>> group(["foo", "bar", "blub", "blah", "bonk"], comp_len)
{'foo': ['foo', 'bar'], 'blub': ['blub', 'blah', 'bonk']}

使用key函数,将每个元素映射到某个hashable值会更好:

def group(elements, key_func):
    groups = {}
    for x in elements:
        key = key_func(x)
        if key in groups:
            groups[key].append(x)
        else:
            groups[key] = [x]
    return groups

示例:

>>> group(["foo", "bar", "blub", "blah", "bonk"], len)
{3: ['foo', 'bar'], 4: ['blub', 'blah', 'bonk']}

答案 1 :(得分:2)

一种方法是使用现成的集合合并算法(无论如何它经常出现在你的工具箱中),只需使用你的awk -vOFS="\t" 'NR==1{$1="Time";$2=""} {print $1,$2,$9}' 函数将数据输入其中。也就是说,我们将其视为集合/连接组件问题,其中每个对象都是一个节点,compare表示节点之间存在边缘。

N.B。我假设传递性,compare(x,y) == True暗示compare(x,y) and compare(x,z)。如果那不是真的,那就是你自己。 : - )

具体:

compare(y,z)

给出了

from itertools import combinations

def consolidate(sets):
    # http://rosettacode.org/wiki/Set_consolidation#Python:_Iterative
    setlist = [s for s in sets if s]
    for i, s1 in enumerate(setlist):
        if s1:
            for s2 in setlist[i+1:]:
                intersection = s1.intersection(s2)
                if intersection:
                    s2.update(s1)
                    s1.clear()
                    s1 = s2
    return [s for s in setlist if s]

def groupify(seq, comp_func):
    nodes = [{x} for x in seq]
    edges = [{x,y} for x,y in combinations(seq,2) if comp_func(x,y)]
    return [list(g) for g in consolidate(nodes + edges)]

不可否认,这并非超级简洁,但请记住,对于我来说,>>> def compare(x,y): return max(x)==max(y) >>> res = groupify(["aaa","abc","a","cab","e","eaab","h"], compare) >>> res [['h'], ['a', 'aaa'], ['cab', 'abc'], ['eaab', 'e']] 只是我在这一点上导入的东西(因为它非常方便),所以从我的角度来看,这只需要几行。 / p>