#python #pandas #function #statistics
#python #pandas #функция #Статистика
Вопрос:
Я работаю в финансовой организации. В нашей таблице транзакций мы отслеживаем остатки клиентов только тогда, когда они совершают транзакции. Например, если клиент открыл счет 1 октября с 200 долларами, а затем снял 50 долларов 8 октября, у него будет всего две записи в таблице транзакций: одна для 2020/10/01, а другая для 2020/10/8. Теперь в центре внимания этого вопроса находятся остатки на момент закрытия. Следуя этой аналогии, если мы используем сегодняшний день в качестве даты отсечения, вы согласитесь, что у клиента был бы баланс закрытия в размере 200 долларов США в течение 7 дней (2020/10/8 — 2020/10/1) и 150 долларов США в течение оставшихся 29 дней.
Теперь я не уверен, как написать эту функцию. Я сталкивался с ошибками, и я был бы признателен, если кто-нибудь сможет помочь мне с кодом python и соответствующими комментариями, чтобы это стало для меня полезным опытом обучения.
Это образец набора данных, который у меня есть:
sample_df = pd.DataFrame({'ID': [15, 16, 15, 15, 16, 17, 17, 16],
'Calendar_Date': ['2020-10-10', '2020-10-12', '2020-10-12', '2020-10-22', '2020-10-28', '2020-10-30', '2020-11-03', '2020-11-04'] ,
'Closing_Balance': [10000, 3000, 6000, 5100, 14500, 25000, 13000, 9000]})
и это результат, который я ожидаю:
result_df = pd.DataFrame({'ID':[15, 16, 17],
'Total_Days': [26, 24, 6],
'Average_Account_Balance': [5823.08, 6375.00, 19000]})
Для ясности: вот как я пришел к результату_df:
Когда ID = 15, Total_Days = (2 10 15) = 27; Average_Account_Balance Средний баланс счета = ((10000 * 2) (6000 * 10) (5100 * 15))/27 = 156500/27 = 5796.3
когда ID = 16, Total_Days = (16 7 2) = 25; Average_Account_Balance Средний баланс счета = ((3000 * 16) (14500 * 7) (9000 * 2))/25 = 167500/25 = 6700.00
когда ID = 17, Total_Days = (4 3) = 7;
Average_Account_Balance = ((25000 * 4) (13000 * 3))/7 = 139000/7 = 19857.14
Мне нужно, чтобы решение было эффективным с точки зрения вычислений, потому что вы можете догадаться, сколько транзакций у нас в нашей БД. Пожалуйста, не стесняйтесь задавать дополнительные вопросы, если вам не ясно, что здесь указано или подразумевается. Спасибо!
Ответ №1:
Вы можете разбить эту проблему на несколько шагов. Во-первых, нам нужно создать несколько новых столбцов во фрейме данных:
- Найдите количество дней с каждой даты до даты окончания (сегодня в вашем примере).
- Внутри каждой группы
"ID"
найдите разницу между предыдущими вычисляемыми столбцами, чтобы получить количество дней между транзакциями. Затем мы используемfillna
метод для заполнения оставшихся различий в датах (например, используяdiff
, мы получаем разницу между строками, но мы упускаем разницу между самой последней датой в пределах"ID"
и сегодняшней датой). Это создает нам правильный"days between transaction"
столбец - Вычислите столбец взвешенного баланса: просто умножьте
"Closing_Balance" by the newly created
столбец «дни между транзакциями»
sample_df["days_from_today"] = (pd.to_datetime("11/06/2020").normalize() - sample_df["Calendar_Date"]).dt.days
sample_df["days_between_transactions"] = (sample_df.groupby("ID")["days_from_today"]
.diff(-1)
.fillna(sample_df["days_from_today"])
.astype(int))
sample_df["weighted_balance"] = sample_df["Closing_Balance"] * sample_df["days_between_transactions"]
print(sample_df)
ID Calendar_Date Closing_Balance days_from_today days_between_transactions weighted_balance
0 15 2020-10-10 10000 27 2 20000
1 16 2020-10-12 3000 25 16 48000
2 15 2020-10-12 6000 25 10 60000
3 15 2020-10-22 5100 15 15 76500
4 16 2020-10-28 14500 9 7 101500
5 17 2020-10-30 25000 7 4 100000
6 17 2020-11-03 13000 3 3 39000
7 16 2020-11-04 9000 2 2 18000
Теперь, когда мы создали наши дополнительные столбцы, мы можем выполнить groupby -> aggregation
операцию для получения sum
нашего "weighted_balance"
столбца и разделить его на max
из "days_from_today"
для каждого уникального "ID"
aggregated_df = sample_df.groupby("ID").agg(
weighted_total_account_balance=("weighted_balance", "sum"),
total_days=("days_from_today", "max")
)
aggregated_df["average_account_balance"] = aggregated_df["weighted_total_account_balance"] / aggregated_df["total_days"]
print(aggregated_df)
weighted_total_account_balance total_days average_account_balance
ID
15 156500 27 5796.296296
16 167500 25 6700.000000
17 139000 7 19857.142857
Я заметил, что в наших результатах есть небольшие расхождения, я полагаю, это может быть связано с различиями в наших часовых поясах (сегодня для меня 11/6/2020, не уверен, какое время / день для вас), поэтому наши «total_days» могут отличаться.
Кроме того, если ваши данные очень большие, я бы рекомендовал использовать DataFrame.eval
для выполнения арифметических операций.
Комментарии:
1. Привет, Кэмерон, спасибо тебе за это. У нас есть та же дата, которая также равна 6.11.2020, но я думаю, что вы принимаете во внимание сегодняшний день, чего не должно быть, поскольку мы не знаем, совершит ли кто-либо из клиентов транзакцию сегодня. Предполагается, что это ситуация типа T-1 или D-1.
2. Привет @Cameron Riddell, я был так утомлен, когда делал свои вычисления, и это повлияло на мои цифры. Да, ваше решение сработало. Большое вам спасибо! Я собираюсь отредактировать свой вопрос, чтобы он никого не вводил в заблуждение в будущем.
3. Потрясающе! Поскольку вам показалось, что это сработало, не могли бы вы выбрать этот ответ как правильный, чтобы другие, у кого есть подобный вопрос, могли быстро найти решение?
4. Riddwell, ГОТОВО!