使用生成器按对象的数量对对象列表进行排序

时间:2018-11-17 14:20:05

标签: python generator itertools

我有一个对象列表,其数量在数千到数万之间。这些对象可以被认为是我希望根据他们的得分进行排名的人。

因此,首先将它们按年龄分组,然后按性别分组。在每个点都提供与该年龄/性别类别相对应的排名。对象上的字段为age_groupgender。因此,您首先需要收集具有30-39年龄段的所有人,然后是该年龄段的所有男性(M)和所有女性(W)。

在这些点上的每个位置创建一个新列表会占用大量内存,因此我尝试使用生成器和来分组使用原始列表。所以我有一个函数可以做到这一点;

def group_standings(_standings, field):
    """ sort list of standings by a given field """
    getter = operator.attrgetter(field)
    for k, g in itertools.groupby(_standings, getter):
        yield list(g)


def calculate_positions(standings):
    """
    sort standings by age_group then gender & set position based on point value 
    """
    for age_group in group_standings(standings, 'age_group'):

        for gender_group in group_standings(age_group, 'gender'):

            set_positions(
                standings=gender_group,
                point_field='points',
                position_field='position',
            )

要使set_positions正常运行,它需要整个组,以便可以按point_field值进行排序,然后设置position_field值。

调试生成器时,groupby并没有像我期望的那样收集所有与键匹配的对象。输出类似于;

DEBUG generating k 30-39
DEBUG generating g [<Standing object at 0x7fc86fedbe10>, <Standing object at 0x7fc86fedbe50>, <Standing object at 0x7fc86fedbe90>]

DEBUG generating k 20-29
DEBUG generating g [<Standing object at 0x7fc86fedbed0>]

DEBUG generating k 30-39
DEBUG generating g [<Standing object at 0x7fc86fedbf10>]

DEBUG generating k 20-29
DEBUG generating g [<Standing object at 0x7fc86fedbf50>, <Standing object at 0x7fc86fedbf90>, <Standing object at 0x7fc86fedbfd0>, <Standing object at 0x7fc856ecc050>, <Standing object at 0x7fc856ecc090>, <Standing object at 0x7fc856ecc0d0>, <Standing object at 0x7fc856ecc110>, <Standing object at 0x7fc856ecc150>, <Standing object at 0x7fc856ecc190>, <Standing object at 0x7fc856ecc1d0>]

要确认,要使set_positions起作用,生成器提供的列表将需要包含20-29年龄组中的所有对象,但是如上所述,该组中的对象位于多个列表的迭代。

2 个答案:

答案 0 :(得分:4)

之所以会这样,是因为groupby函数假定输入的可迭代输入已按键排序(请参见documentation)。它是为提高性能而设计的,但令人困惑。 另外,我不会将g强制转换为group_standings函数中的列表,而仅在将gender_group传递给set_positions时才应用。

答案 1 :(得分:1)

groupby适用于相邻元素

根据@MikhailBerlinkov's answergroupby汇总相同的连续项目,可以选择使用key参数进行比较。

看一个例子可能会有所帮助:

from itertools import groupby

L = [1, 1, 1, 2, 2, 2, 1, 1]

res = [list(j) for _, j in groupby(L)]

[[1, 1, 1], [2, 2, 2], [1, 1]]

如您所见,1值的组被分为两个单独的列表。

分组前先排序

相反,您可以在分组之前对对象列表进行排序。对于长度为 n 的大量对象,这需要O( n log n )时间。这是一个示例(使用与之前相同的L):

L_sorted = sorted(L)

res = [list(j) for i, j in groupby(L_sorted)]

[[1, 1, 1, 1, 1], [2, 2, 2]]