Python 根据其他字段重新计算数据帧字段的更好方法

Python 根据其他字段重新计算数据帧字段的更好方法,python,python-3.x,pandas,numpy,Python,Python 3.x,Pandas,Numpy,我是新来的。 考虑到费用和一段时期内的增长情况,我想估算长期内的流入付款价值。 我只使用了一次付款(流入)进行测试。 有时fee2可以应用到周期n-t。i、 e.不是整个时期,n 我是这样做的,只是想知道是否有更好的方法可以在不循环的情况下重新计算值 电子表格中的示例: Python代码: import pandas as pd import numpy as np def getCashFlows(): term = 2 growthRate = (1+0.06)**(1/12

我是新来的。 考虑到费用和一段时期内的增长情况,我想估算长期内的流入付款价值。 我只使用了一次付款(流入)进行测试。 有时fee2可以应用到周期n-t。i、 e.不是整个时期,n

我是这样做的,只是想知道是否有更好的方法可以在不循环的情况下重新计算值

电子表格中的示例:

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

真正需要的唯一初始输入是流入的初始值。根据行索引,其他所有操作都可以简化为重复一定次数的操作。数据框中的一些列实际上只是常量

下面是一个解决方案,它阐明了计算数据帧的每一行所需的操作:

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
我发现将这一切表示为一个类比较容易,但只需在类之外定义变量,然后运行
update()
函数就足够简单了

更新
以下是此解决方案背后的更多解释:

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

这意味着,就我们需要完成的计算而言,我们可以将“兴趣矩阵”限制在列
Value\u t\u 1
growth
ValuePlusGrowth
fee1
Value\u 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
的操作

每一个值都可以用我们需要乘以的值来表示,以得到所需的值(后面我将讨论一个例外)。例如:

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
的关系

然后:

下一步:

现在,对于每一行,我们为每一列提供了一组更新操作
ops
。假设我们有上一行的
t
,我们可以用以下内容填充每一行的值:

new_row = t * ops
我们仍然需要从
new\u t
中减去
fee2
,而这并不完全符合到目前为止的一系列乘法运算。但我们可以坚持我们的矢量化公式并定义:

fee2 = (0,0,0,0,30)
在每个
new\u row
之后,我们从
new\u row
向量中减去
fee2
向量,这实际上只是根据需要从
new\u t
中减去
fee2

new_row = t * ops - fee2
此时,我们只需要一个函数,它以
t=10000
开始,并继续执行
new_row
公式,建立在前一行的基础上,直到达到所需的迭代次数。为此,我选择了一种递归策略,并在每个递归步骤中将每个
新行
保存到一个数据帧中


最后,由于我设置了
t=10000
而不是
Inflow1=10000
,这意味着第一个
t1
值被错误地设置为
10000
。在
update()
函数的末尾,我们将第一个
t1
值设置回
0

只需去掉def&for并运行其余的代码。循环是隐含的。您有“for i”,但没有“i”,所以您所做的只是重复完全相同的代码24次,而不是一次,并且它在整个df上每隔一天运行一次time@JohnE-实际上不完全是。起初我也这么想,但注意到第一行中的
shift()
。每个循环都在值上向前看。在这样做的过程中,OP实际上是在24个周期内累积这些值。谢谢@andrew_reece,但我正在努力运行代码,我在引用一些变量之前修复了其他简单错误,例如缺少self…但是对update(self,t,n,df=None)、update(row.iloc[-1],n+1,self.n_iter,tmp)的递归调用有一个额外的值(row.iloc[-1])对此我很抱歉,在我转换为基于类的方法之前,我的环境中有一些变量,在我发布之前,我没有清除所有要测试的内容。是我的错。现在更新了,我已经从一个新的内核开始了这段代码,并确认一切都正常工作。非常感谢…花了更长的时间,因为我想确保我理解它…不客气,我真的很喜欢这个挑战。我补充说
new_row = t * ops
fee2 = (0,0,0,0,30)
new_row = t * ops - fee2