Python Pandas groupby: как сделать условное агрегирование в зависимости от другого столбца - PullRequest
1 голос
/ 23 марта 2020

Я хотел бы использовать групповую работу Panda с несколькими функциями агрегации, но также с включением условных операторов для агрегации. Представьте себе эти данные в качестве примера:

df = pd.DataFrame({
    'id': ['a', 'a', 'a', 'b', 'b'],
    'type': ['in_scope', 'in_scope', 'exclude', 'in_scope', 'exclude'],
    'value': [5, 5, 99, 20, 99]
})
INPUT DATA:
| id | in_scope | value |
|----|----------|-------|
| a  | True     | 5     |
| a  | True     | 5     |
| a  | False    | 99    |
| b  | True     | 20    |
| b  | False    | 99    |

И я хочу создать группу Pandas следующим образом:

df.groupby('id').agg(
    num_records=('id', 'size'),
    sum_value=('value', np.sum)
)
OUTPUT OF SIMPLE GROUPBY:
| id | num_records | sum_value |
|----|-------------|-----------|
| a  | 3           | 109       |
| b  | 2           | 119       |

Однако я бы хотел чтобы сделать сумму в зависимости от условия, а именно, что должны использоваться только записи "in_scope", которые определены как True в столбце in_scope. Обратите внимание, что первое объединение должно по-прежнему использовать всю таблицу. Короче говоря, это желаемый результат:

DESIRED OUTPUT OF GROUPBY:
| id | num_records | sum_value_in_scope |
|----|-------------|--------------------|
| a  | 3           | 10                 |
| b  | 2           | 20                 |

Я думал о передаче двух аргументов лямбда-функции, но у меня ничего не получилось. Конечно, это можно решить, выполнив две отдельные групповые операции с отфильтрованными и нефильтрованными данными, а затем объедините их вместе. Но я надеялся, что есть более короткий и элегантный способ.

1 Ответ

1 голос
/ 23 марта 2020

К сожалению, вы не можете сделать это с агрегатом, однако вы можете сделать это за один шаг с применением и пользовательской функцией:

def f(x):
    d = {}
    d['num_records'] = len(x)
    d['sum_value_in_scope'] = x[x.in_scope].value.sum()
    return pd.Series(d, index=['num_records', 'sum_value_in_scope'])

df.groupby('id').apply(f)

Поскольку столбец df.in_scope уже является логическим, вы можете использовать его в качестве маски непосредственно для фильтрации суммируемых значений. Если столбец, с которым вы работаете, не является логическим, лучше использовать df.query('<your query here>'), чтобы получить подмножество данных (есть скрытые оптимизации, которые делают его быстрее, чем большинство других методов).

...