真正需要的是用於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
大部分是空的。唯一完全非零的列是從未使用的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
我們仍然需要從new_t
減去fee2
,並且不完全適合什麼,直到這一點,曾經是一系列乘法運算。但是,我們可以用我們的矢量配方堅持和定義:
fee2 = (0,0,0,0,30)
每個new_row
後,我們減去從new_row
載體,無論你希望真的只是減去fee2
從new_t
的fee2
載體。
new_row = t * ops - fee2
在這一點上,我們只需要與t = 10000
開始並保持執行new_row
公式,建立在每一個上一行的功能,直到我們達到我們所期望的迭代次數。我選擇了一個遞歸策略來做到這一點,並在每個遞歸步驟中將每個new_row
保存到數據幀中。
最後,因爲我有點通過設置t = 10000
而不是Inflow1 = 10000
濫用你原來的符號,這意味着第一t1
值不正確地設置爲10000
。在update()
函數結束時,我們將第一個t1
值設置回0
。
剛剛擺脫def&for並運行其餘的代碼。循環是隱含的。你有「爲我」,但沒有「我」,所以你所做的只是重複完全相同的代碼24次而不是一次,它每次都在整個DF上運行 – JohnE
@JohnE - 實際上並不完全。起初,我認爲是相同的,但注意第一行中的「shift()」。每個循環都在* Value_t *上向前看。而這樣做,OP實際上是在24個時期內積累這些價值。 – Parfait