Рекурсивное применение к группам DataFrame, вызывающее ошибку переиндексации - PullRequest
0 голосов
/ 05 июня 2019

Я хочу выделить некоторое количество «единиц» для каждой группы DataFrame, которое выглядит примерно так:

       limit  allocation  spaceLeft
Group                              
A        5.0         0.0        5.0
A        3.0         0.0        3.0
A        7.0         0.0        7.0
B        1.0         0.0        1.0
B        2.0         0.0        2.0
B        4.0         0.0        4.0
B        6.0         0.0        6.0

... которое может быть создано:

df = pd.DataFrame(data=[('A', 5.0, 0.0),
                        ('A', 3.0, 0.0),
                        ('A', 7.0, 0.0),
                        ('B', 1.0, 0.0),
                        ('B', 2.0, 0.0),
                        ('B', 4.0, 0.0),
                        ('B', 6.0, 0.0)],
                  columns=('Group', 'limit', 'allocation')).set_index('Group')
df['spaceLeft'] = df['limit'] - df['allocation']

Ограничение состоит в том, что распределение единиц должно быть как можно более равномерным в строках каждой группы, но не может превышать limit для каждой строки.Так, например, если у нас есть 10 единиц, то окончательное, правильное распределение для группы A будет:

       limit  allocation  spaceLeft
Group                              
A        5.0         3.5        1.5
A        3.0         3.0        0.0
A        7.0         3.5        3.5

Я написал рекурсивную функцию для этого:

unitsToAllocate = 10.0
def f(g):
    allocated = g['allocation'].sum()
    unitsLeft = unitsToAllocate - allocated
    if unitsLeft > 0:
        g['spaceLeft'] = g['limit'] - g['allocation']
        # "Quantum" is the space left in the smallest bin with space remaining
        quantum = g[g['spaceLeft'] > 0]['spaceLeft'].min()
        # Distribute only as much as will fill next bin to its limit
        alloc = min(unitsLeft / g[g['spaceLeft'] > 0]['spaceLeft'].count(), quantum)
        g.loc[g['spaceLeft'] > 0, 'allocation'] = g[g['spaceLeft'] > 0]['allocation'] + alloc
        f(g)
    else:
        return g

Если я вручную, итеративно запускаю внутреннюю логику f в одной группе, такой как group = df.groupby('Group').get_group('A'), тогда она работает.(То есть, он выдает правильный результат для A, показанного выше.)

Но если я вызову f, как задумано с помощью df.groupby('Group').apply(f), он завершится неудачно с:

ValueError: cannot reindex from a duplicate axis.

Что не так?

И есть ли более пандальный подход к этому алгоритму?

1 Ответ

0 голосов
/ 05 июня 2019

Глупая ошибка в логике рекурсии: Обе ветви f(g) должны возвращать группу.

Следующий код работает:

def f(g):
    allocated = g['allocation'].sum()
    unitsLeft = unitsToAllocate - allocated
    if unitsLeft > 0:
        g['spaceLeft'] = g['limit'] - g['allocation']
        quantum = g[g['spaceLeft'] > 0]['spaceLeft'].min()
        alloc = min(unitsLeft / g[g['spaceLeft'] > 0]['spaceLeft'].count(), quantum)
        g.loc[g['spaceLeft'] > 0, 'allocation'] = g[g['spaceLeft'] > 0]['allocation'] + alloc
        return f(g)  # <-- FIXED THIS LINE
    else:
        return g
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...