我有一个对象列表,其数量在数千到数万之间。这些对象可以被认为是我希望根据他们的得分进行排名的人。
因此,首先将它们按年龄分组,然后按性别分组。在每个点都提供与该年龄/性别类别相对应的排名。对象上的字段为age_group
和gender
。因此,您首先需要收集具有30-39
年龄段的所有人,然后是该年龄段的所有男性(M
)和所有女性(W
)。
在这些点上的每个位置创建一个新列表会占用大量内存,因此我尝试使用生成器和itertools来分组使用原始列表。所以我有一个函数可以做到这一点;
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
年龄组中的所有对象,但是如上所述,该组中的对象位于多个列表的迭代。
答案 0 :(得分:4)
之所以会这样,是因为groupby函数假定输入的可迭代输入已按键排序(请参见documentation)。它是为提高性能而设计的,但令人困惑。
另外,我不会将g
强制转换为group_standings
函数中的列表,而仅在将gender_group
传递给set_positions
时才应用。
答案 1 :(得分:1)
groupby
适用于相邻元素根据@MikhailBerlinkov's answer,groupby
汇总相同的连续项目,可以选择使用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]]