Сортировать список объектов по количеству атрибутов с помощью генератора - PullRequest
0 голосов
/ 17 ноября 2018

У меня есть список объектов, число которых будет составлять от тысячи до десятков тысяч.Об этих объектах можно думать как о людях, которых я рассчитываю оценивать, основываясь на набранных ими баллах.

Итак, прежде всего, они разбиты на группы по возрасту, затем полу и т. Д. В каждой точке рейтингапредоставляется в соответствии с этой возрастной / гендерной категорией.Поля на объектах: age_group и gender.Таким образом, вы должны сначала собрать всех, у кого есть возрастная группа 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 голосов
/ 17 ноября 2018

groupby работает на смежных элементах

Согласно @ ответу Михаила Берлинкова , 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]]
0 голосов
/ 17 ноября 2018

Это происходит потому, что функция groupby предполагает, что итеративный ввод уже отсортирован по ключу (см. документация ). Это сделано для производительности, но сбивает с толку. Кроме того, я не буду приводить g к списку в функции group_standings, но применяю его только тогда, когда вы передаете gender_group в set_positions.

...