我是python熊猫的新手。
我想考虑一段时间内的费用和增长情况,估算长期内流入付款的价值。
我只用一笔付款(流入)进行测试。
有时,费用2最多可以适用于周期n-t。即不是整个时期。
我像下面那样做,只是想知道是否有更好的方法可以在不循环的情况下重新计算值?
电子表格中的示例:
Python代码:
import pandas as pd
import numpy as np
def getCashFlows():
term = 2
growthRate = (1+0.06)**(1/12) - 1
df = pd.DataFrame(list(range(1,term*12+1)), columns=['t'])
df['Value_t_1'] = 0
df['Inflow1']=0
df['growth']=0
df['ValuePlusGrowth'] = 0
df['fee1']=0
df['fee2']=30
df['Value_t']=0
df.set_value(0, 'Inflow1', 10000)
for i in range(0,term*12):
df['Value_t_1'] = df['Value_t'].shift()
df['Value_t_1'].fillna(0,inplace=True)
df['growth'] = (df['Value_t_1'] + df['Inflow1'])*growthRate
df['ValuePlusGrowth'] = df['Value_t_1']+df['Inflow1']+df['growth']
df['fee1']=df['ValuePlusGrowth']*0.5/100
df['Value_t'] = df['ValuePlusGrowth'] - df['fee1'] - df['fee2']
return df
最佳答案
真正需要的唯一初始输入是inflow
的初始值。根据行索引,可以将其他所有内容简化为可重复执行一定次数的操作。数据框中的某些列实际上只是常量。
这是一个解决方案,阐明了计算数据帧每一行所需的操作:
import pandas as pd
class GrowthTracker(object):
def __init__(self, n_iter):
self.colnames = ['Value_t_1', 'growth', 'ValuePlusGrowth', 'fee1', 'Value_t']
self.data = None
self.fee1_mult = 0.5/100
self.fee2 = (0,0,0,0,30)
self.growthRate = (1+0.06)**(1/12) - 1
self.n_iter = n_iter
self.ops = pd.Series([1, # Value_t_1
self.growthRate, # growth
(1 + self.growthRate), # ValuePlusGrowth
(1 + self.growthRate) * self.fee1_mult, # fee1
(1 + self.growthRate) * (1 - self.fee1_mult) # Value_t
])
def update(self, t, n, df=None):
row = self.ops.mul(t).subtract(self.fee2)
tmp = pd.concat([df, row], axis = 1, ignore_index=True)
if n < self.n_iter:
self.data = self.update(row.iloc[-1], n+1, tmp)
return self.data
else:
tmp.iloc[0,0] = 0 # remove the initial 10000 from Value_t_1
self.data = tmp.T
self.data.columns = self.colnames
return self.data
现在只需设置初始值,实例化
GrowthTracker
对象和update()
:total_iter = 23
tracker = GrowthTracker(n_iter=total_iter)
inflow = 10000
start_index = 0
tracker.update(t=inflow, n=start_index)
tracker.data
Value_t_1 growth ValuePlusGrowth fee1 Value_t
0 0.000000 48.675506 10048.675506 50.243378 9968.432128
1 9968.432128 48.521847 10016.953976 50.084770 9936.869206
2 9936.869206 48.368213 9985.237419 49.926187 9905.311232
3 9905.311232 48.214603 9953.525835 49.767629 9873.758206
4 9873.758206 48.061017 9921.819223 49.609096 9842.210127
5 9842.210127 47.907455 9890.117583 49.450588 9810.666995
6 9810.666995 47.753918 9858.420912 49.292105 9779.128808
7 9779.128808 47.600404 9826.729212 49.133646 9747.595566
8 9747.595566 47.446914 9795.042480 48.975212 9716.067268
9 9716.067268 47.293449 9763.360716 48.816804 9684.543913
10 9684.543913 47.140007 9731.683920 48.658420 9653.025500
11 9653.025500 46.986590 9700.012090 48.500060 9621.512030
12 9621.512030 46.833196 9668.345226 48.341726 9590.003500
13 9590.003500 46.679827 9636.683327 48.183417 9558.499910
14 9558.499910 46.526482 9605.026392 48.025132 9527.001260
15 9527.001260 46.373160 9573.374420 47.866872 9495.507548
16 9495.507548 46.219863 9541.727411 47.708637 9464.018774
17 9464.018774 46.066590 9510.085364 47.550427 9432.534937
18 9432.534937 45.913341 9478.448278 47.392241 9401.056037
19 9401.056037 45.760116 9446.816152 47.234081 9369.582072
20 9369.582072 45.606915 9415.188986 47.075945 9338.113041
21 9338.113041 45.453737 9383.566779 46.917834 9306.648945
22 9306.648945 45.300584 9351.949529 46.759748 9275.189781
23 9275.189781 45.147455 9320.337237 46.601686 9243.735551
我发现将其全部表达为一个类比较容易,但是只需在类外部定义变量然后运行
update()
函数就足够简单了。更新
以下是此解决方案的更多说明:
初始数据帧
df
大部分为空。唯一完全非零的列是t
(从未使用)和fee2
(这是一个常量(fee2 = 30
))。 df
的其余全部以零值开始,但Inflow1
中的第一个单元格除外-它的第一个值为10000
,其余值为零。这意味着,根据需要完成的计算,我们可以将“感兴趣的矩阵”限制为列
Value_t_1
,growth
,ValuePlusGrowth
,fee1
和Value_t
。我们可以将第一个
Inflow1
值视为种子-其他所有内容只是对数字10000
执行的一系列操作。 (实际上,我们实际上不需要Inflow1
作为字段,因为它的所有其他值在整个计算过程中都保持为零。)在循环中,最初使用其他列的值更新了列。这是有道理的,可能也是我本来会做的-看起来整洁而高效。但是,请记住,每次更新实际上只是一串数学运算,其沿袭可以追溯到原始的
10000
。写出每个列更新的实际操作,而不是使用其他列名,显示了如何简化每个更新操作。首先,一些速记符号:
t = Value_t from previous row (in case of the first row, Value_t = Inflow1 = 10000)
t1 = Value_t_1
g = growth
inf = Inflow1
vpg = ValuePlusGrowth
gr = growthRate # gr is a constant: (1+0.06)**(1/12) - 1
f1X = 0.5/100
new_t = Value_t for current row
我们从
t = 10000
开始。其他所有操作均在t
上进行。每个值都可以用我们要乘以
t
的值来表示,以便获得所需的值(一个例外,我稍后会介绍)。因此,例如:df['Value_t_1'] = df['Value_t'].shift()
df['Value_t_1'].fillna(0,inplace=True)
# equivalent to:
t1 = 1 * t # recall t is the shifted Value_t from the previous row
请记住,我们只需要输入一次种子值
t
,然后只需对种子进行操作即可填充所有df
。这意味着循环中的操作可以表示为“为了获得正确的列值而需要乘以t的项”。因此,尽管我们已经证明了t1 = 1 * t
,但对我们而言,考虑t1 = 1
更为有用-最终我们将其乘以t
,但该等式的右侧表示t1
与t
的关系>。然后:
t1 = 1
下一个:
# Inflow1 is always 0, except for its initial value which we capture in initial t, so:
df['growth'] = (df['Value_t_1'] + df['Inflow1'])*growthRate
# becomes:
g = t1 * gr
# with t1 = 1
g = gr
# we know t1 = 1, and inf is never used as a multiplier, so:
df['ValuePlusGrowth'] = df['Value_t_1']+df['Inflow1']+df['growth']
# becomes:
vpg = 1 + g = 1 + gr
df['fee1']=df['ValuePlusGrowth']*0.5/100
# becomes:
fee1 = vpg * f1X = (1 + gr) * f1X
# we'll ignore subtracting fee2 for now, see notes below.
df['Value_t'] = df['ValuePlusGrowth'] - df['fee1'] - df['fee2']
# becomes:
new_t = vpg - fee1 = (1 + gr) - ((1 + gr) * f1X) = (1 + gr) * (1 - f1X)
ops = (t1, g, vpg, fee1, new_t)
现在,对于每一行,我们对每一列都有一组更新操作
ops
。鉴于上一行有t
,我们可以使用以下命令为每一行填充值:new_row = t * ops
我们仍然需要从
fee2
中减去new_t
,直到现在为止,这还不能很好地适应一系列乘法运算。但是我们可以坚持我们的向量化公式并定义:fee2 = (0,0,0,0,30)
在每个
new_row
之后,我们从fee2
向量中减去new_row
向量,这实际上只是根据需要从fee2
中减去new_t
。new_row = t * ops - fee2
在这一点上,我们只需要一个以
t = 10000
开头并继续执行new_row
公式的函数,该函数建立在上一行的每一行上,直到达到所需的迭代次数为止。我选择了一种递归策略来执行此操作,并在每个递归步骤中将每个new_row
保存到数据帧。最后,由于我通过设置
t = 10000
而不是Inflow1 = 10000
在某种程度上滥用了原始符号,因此这意味着第一个t1
值错误地设置为10000
。在update()
函数的末尾,我们将第一个t1
值重新设置为0
。