2017-08-13 41 views
2

我是新的python熊猫。 我想估计一段时间内流入支付的价值(考虑费用和增长)。 我只用一次付款(流入)来测试。有时fee2可以应用到n-t期。即整个期间都不是,n。更好的方式来重新计算大熊猫数据框字段取决于其他领域

我做了如下,只是想知道是否有更好的方法来重新计算没有循环的值?

实施例的电子表格: enter image description here

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 
+0

刚刚摆脱def&for并运行其余的代码。循环是隐含的。你有“为我”,但没有“我”,所以你所做的只是重复完全相同的代码24次而不是一次,它每次都在整个DF上运行 – JohnE

+1

@JohnE - 实际上并不完全。起初,我认为是相同的,但注意第一行中的“shift()”。每个循环都在* Value_t *上向前看。而这样做,OP实际上是在24个时期内积累这些价值。 – Parfait

回答

2

真正需要的是用于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()函数即可。

UPDATE
下面是该解决方案的背后更多的解释:

初始数据帧df大部分是空的。唯一完全非零的列是从未使用的tfee2,这是一个常数(fee2 = 30)。 df的整个剩余部分以零值开始,除Inflow1中的第一个单元外 - 其第一个值为10000,其余值为零。

这意味着,在我们所需要完成的计算而言,我们可以限制我们的“利益矩阵”的列Value_t_1growthValuePlusGrowthfee1Value_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,但该等式的右边代表t1t的关系。

然后:

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 

我们仍然需要从new_t减去fee2,并且不完全适合什么,直到这一点,曾经是一系列乘法运算。但是,我们可以用我们的矢量配方坚持和定义:

fee2 = (0,0,0,0,30) 

每个new_row后,我们减去从new_row载体,无论你希望真的只是减去fee2new_tfee2载体。

new_row = t * ops - fee2 

在这一点上,我们只需要与t = 10000开始并保持执行new_row公式,建立在每一个上一行的功能,直到我们达到我们所期望的迭代次数。我选择了一个递归策略来做到这一点,并在每个递归步骤中将每个new_row保存到数据帧中。

最后,因为我有点通过设置t = 10000而不是Inflow1 = 10000滥用你原来的符号,这意味着第一t1值不正确地设置为10000。在update()函数结束时,我们将第一个t1值设置回0

+0

感谢@andrew_reece,但我努力运行代码,我修复了其他简单的错误,如在引用一些变量之前丢失self ...但是递归调用update(self,t,n,df = None),update(row。 iloc [-1],n + 1,self.n_iter,tmp)有一个额外的值(row.iloc [-1])我不能匹配 – Mosd

+0

对不起,我有一些变量在我的环境中到基于班级的方法,而且在发布之前我没有清楚所有要测试的内容。我的错。现在更新了,我从一个新的内核开始了这段代码,并确认所有内容都以发布的方式工作。 –

+0

非常感谢...花了更长的时间,因为我想确保我明白了... – Mosd