Группировка с использованием пользовательской функции для агрегации
Имеется DataFrame такого вида:
Необходимо сгруппировать данные по филиалам и получить общую сумму по колонке сумма для каждого филиала. Но кроме суммы нужно получить еще расчетную среднюю величину для каждого филиала, и в этом заключается проблема.
Средняя должна считаться следующим образом: для каждой группы (филиала) считается сумма произведений величины "сумма" на величину "срок" и делится на сумму по колонке "сумма" для этой группы.
Методом проб мне удалось сделать такой рабочий код:
import pandas as pd
df = pd.read_csv('df_samp.csv') # ссылка на файл с фрагментом данных внизу
togroup = df[['филиал', 'сумма', 'срок']][df['срок']>90] # группируем только строки где "срок" > 90
togroup['средн'] = np.NaN # добавил столбец для средних значений
grouped = togroup.groupby('филиал').agg(lambda x: int(sum(x.сумма*x.срок)/sum(x.сумма)))[['сумма', 'средн']]
На выходе получаю следующее:
Собственно сами вопросы:
- как правильно составить функцию для агрегирования (не уверен что мой способ правильный\лучший)?
- как сделать чтобы в колонке сумма была итоговая сумма, а не дублировалось среднее (не получается задать для каждой колонки свою функцию)?
- есть ли способ не вставлять пустой столбец для итоговых данных, а создать его "на лету" при группировке?
UPD: фрагмент данных в формате csv
Ответы (2 шт):
Как я понял, вам нужно получить две новые колонки на основании вычислений. Вероятно, можно сделать так:
res = df[df['срок']>90].groupby('филиал')[['филиал', 'сумма', 'срок']].apply(
lambda x: pd.Series({"сумм": x["сумма"].sum(), "сред": (x["сумма"]*x["срок"]).sum()/x["сумма"].sum()}))
Тогда для вашего примера результат будет таким:
сумм сред
филиал
Алтайский КРТПЦ 736451.97 1488.739564
Волгоградский ОРТПЦ 0.70 1281.000000
Воронежский ОРТПЦ 98343.38 1115.403130
Дальневосточный РЦ 1149160.45 458.215772
Забайкальский КРТПЦ 103737.64 750.595304
Камчатский КРТПЦ 960.44 367.000000
Краснодарский КРТПЦ 482443.26 291.000000
Курганский ОРТПЦ 90024.04 508.825266
Магаданский ОРТПЦ 57254.29 496.717449
Московский РЦ 4129843.28 820.056739
Нижегородский ОРТПЦ 812323.32 169.279333
РТПЦ Еврейской АО 72.00 946.000000
РТПЦ Кабардино-Балкарской Республики 124682.40 504.500000
РТПЦ Карачаево-Черкесской Республики 680824.77 563.927502
РТПЦ Республики Калмыкия 1049.90 1914.730536
РТПЦ Республики Коми 180949.45 1226.852839
РТПЦ Республики Крым 15260435.65 958.266395
РТПЦ Республики Тыва 23280.00 108.000000
РТПЦ Чувашской Республики 3658.38 1845.862666
Саратовский ОРТПЦ 26066.24 141.016411
Свердловский ОРТПЦ 967.23 885.000000
Сибирский РЦ 32272.12 442.000000
Тульский ОРТПЦ 101816.17 494.907639
Урало-Сибирский РЦ 620750.47 500.127947
Челябинский ОРТПЦ 68354.00 797.511367
Разумеется, нужно перепроверить.
Можно сделать так:
res = (df
[['филиал', 'сумма', 'срок']]
.query("срок > 90")
.assign(сред=df["сумма"]*df["срок"])
.groupby("филиал")
.agg(
сумм=("сумма","sum"),
сред=("сред", "sum"))
.eval("сред = сред / сумм"))
результат:
In [129]: res
Out[129]:
сумм сред
филиал
Алтайский КРТПЦ 736451.97 1488.739564
Волгоградский ОРТПЦ 0.70 1281.000000
Воронежский ОРТПЦ 98343.38 1115.403130
Дальневосточный РЦ 1149160.45 458.215772
Забайкальский КРТПЦ 103737.64 750.595304
Камчатский КРТПЦ 960.44 367.000000
Краснодарский КРТПЦ 482443.26 291.000000
Курганский ОРТПЦ 90024.04 508.825266
Магаданский ОРТПЦ 57254.29 496.717449
Московский РЦ 4129843.28 820.056739
Нижегородский ОРТПЦ 812323.32 169.279333
РТПЦ Еврейской АО 72.00 946.000000
РТПЦ Кабардино-Балкарской Республики 124682.40 504.500000
РТПЦ Карачаево-Черкесской Республики 680824.77 563.927502
РТПЦ Республики Калмыкия 1049.90 1914.730536
РТПЦ Республики Коми 180949.45 1226.852839
РТПЦ Республики Крым 15260435.65 958.266395
РТПЦ Республики Тыва 23280.00 108.000000
РТПЦ Чувашской Республики 3658.38 1845.862666
Саратовский ОРТПЦ 26066.24 141.016411
Свердловский ОРТПЦ 967.23 885.000000
Сибирский РЦ 32272.12 442.000000
Тульский ОРТПЦ 101816.17 494.907639
Урало-Сибирский РЦ 620750.47 500.127947
Челябинский ОРТПЦ 68354.00 797.511367

