#python #pandas #dataframe #finance
#python #панды #фрейм данных #финансы
Вопрос:
Я пытаюсь вычислить две совокупные суммы с учетом серии финансовых транзакций. Существует 4 типа транзакций, каждый из которых содержит сумму для транзакции: D — депозит W — снятие G — прибыль L — убыток
Фрейм данных создается следующим образом
import pandas as pd
import numpy as np
data = { 'Type': ['D', 'D', 'W', 'D', 'G', 'G', 'G', 'L', 'W', 'G', 'W', 'G', 'L' ],
'Amount': [10, 10, -5, 10, 5, 5, 5, -5, -10, 10, -25, 25, -30]
}
df = pd.DataFrame(data, columns = ['Type', 'Amount'])
Текущий капитал легко вычислить с помощью cumsum() , который по существу включает все транзакции.
df['Capital'] = df['Amount'].cumsum()
Другой объект, который я хочу вычислить, — это принципал, который представляет сумму денег, вводимую на счет. При этом учитываются только транзакции типа D и W. Я могу сделать простой фильтр здесь, используя:
df['Principal'] = df.apply(lambda row : row['Amount'] if (row['Type'] == 'W' or row['Type'] == 'D') else 0, axis=1).cumsum()
Однако в этом есть проблема. Когда есть выигрыши и есть изъятия, изъятие должно быть снято с прибыли, прежде чем оно повлияет на этого принципала. Результат, приведенный выше, содержит ошибку в приведенном ниже результате (строки 8 и 10):
Type Amount Capital Principal
0 D 10 10 10
1 D 10 20 20
2 W -5 15 15
3 D 10 25 25
4 G 5 30 25
5 G 5 35 25
6 G 5 40 25
7 L -5 35 25
8 W -10 25 15 <- should stays at 25
9 G 10 35 15 <- now wrong because of above
10 W -25 10 -10 <- error escalades
11 G 25 35 -10
12 L -30 5 -10
Я могу получить желаемый результат, выполнив следующее, но это кажется немного уродливым.
Интересно, есть ли какой-нибудь более простой или короткий способ. Я бы предположил, что это обычный расчет в финансах.
df['Principal'] = np.nan
currentPrincipal = 0
for index, row in df.iterrows():
if (row['Type'] == 'D'):
#row['Principal'] = currentPrincipal row['Amount']
df.loc[index, 'Principal'] = currentPrincipal row['Amount']
elif (row['Type'] == 'W' and row['Capital'] <= currentPrincipal):
#row['Principal'] = row['Capital']
df.loc[index, 'Principal'] = row['Capital']
else:
df.loc[index, 'Principal'] = currentPrincipal
currentPrincipal = df.loc[index, 'Principal']
Мне не удалось попытаться использовать apply
, поскольку у нас есть зависимость от предыдущего результата принципала, который необходимо перенести.
Правильный результат:
Type Amount Capital Principal
0 D 10 10 10
1 D 10 20 20
2 W -5 15 15
3 D 10 25 25
4 G 5 30 25
5 G 5 35 25
6 G 5 40 25
7 L -5 35 25
8 W -10 25 25
9 G 10 35 25
10 W -25 10 10
11 G 25 35 10
12 L -30 5 10
Ответ №1:
Вы можете сделать:
# calculate cumulative withdrawals
w = df['Amount'].where(df['Type'].eq('W')).cumsum()
# calculate cumulative deposits
d = df['Amount'].where(df['Type'].eq('D'), 0).cumsum()
# calculate cumulative gain amp; loss
g = df['Amount'].where(df['Type'].isin(['G', 'L']), 0).cumsum()
# calculate principal = deposit net_withdrawal(if any)
df['Principal'] = d (g w).where(lambda x: x < 0).ffill().fillna(0)
Результат:
Type Amount Capital Principal
0 D 10 10 10.0
1 D 10 20 20.0
2 W -5 15 15.0
3 D 10 25 25.0
4 G 5 30 25.0
5 G 5 35 25.0
6 G 5 40 25.0
7 L -5 35 25.0
8 W -10 25 25.0
9 G 10 35 25.0
10 W -25 10 10.0
11 G 25 35 10.0
12 L -30 5 10.0
Комментарии:
1. Спасибо Shubham. Выглядит чище. Знаете ли вы относительную производительность этого? Похоже, что для вычисления всех совокупных сумм требуется много итераций?
2. @phandinhlan Я чувствую, что это должно быть быстрее, поскольку
cumsum
операция по своей сути векторизована в numpy. Но я думаю, вы можете проверить это на своих данных, чтобы увидеть результаты самостоятельно..3. Меня беспокоит то, что
cumsum()
это вызывалось 3 раза по сравнению с одним циклом for, а не то, что каждыйcumsum()
из них не быстрый.