python - 根据其他字段重新计算pandas数据框字段的更好方法

标签 python python-3.x pandas numpy

我是python熊猫的新手。
我想考虑一段时间内的费用和增长情况,估算长期内流入付款的价值。
我只用一笔付款(流入)进行测试。
有时,费用2最多可以适用于周期n-t。即不是整个时期。

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

电子表格中的示例:
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

最佳答案

真正需要的唯一初始输入是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_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


我们仍然需要从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

关于python - 根据其他字段重新计算pandas数据框字段的更好方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45659372/

相关文章:

python - Spyne、TLS 和服务器到客户端的请求

python - 我怎样才能找到所有 ydl_opts

python-3.x - 将 inception 输出导出为 spacy 的训练输入格式

python - 如何找到 pandas 中每一对后续 DataFrame.index 值之间的差异?

python - 数据框未使用值进行更新;输出是警告 'copy of a slide'

python - 'utf- 8' codec can' t 解码字节 0x80

python - 如何使用该列周围(顶部和底部)值的平均值填充该列的 NaN 值?

python - 将宏添加到 Python

django - <class 'astromatchapp.report.admin.user.ReportUserAdmin'> 中定义的 Action 的 __name__ 属性必须是唯一的

Python Pandas 加入索引值