Python 在“应用”中也计算上一行值时,是否有方法在dataframe.apply中使用上一行值?
我有以下数据帧:Python 在“应用”中也计算上一行值时,是否有方法在dataframe.apply中使用上一行值?,python,pandas,dataframe,for-loop,iteration,Python,Pandas,Dataframe,For Loop,Iteration,我有以下数据帧: Index_Date A B C D =============================== 2015-01-31 10 10 Nan 10 2015-02-01 2 3 Nan 22 2015-02-02 10 60 Nan 280 2015-02-03 10 100 Nan 250 要求: Index_Date A B C D ==
Index_Date A B C D
===============================
2015-01-31 10 10 Nan 10
2015-02-01 2 3 Nan 22
2015-02-02 10 60 Nan 280
2015-02-03 10 100 Nan 250
要求:
Index_Date A B C D
===============================
2015-01-31 10 10 10 10
2015-02-01 2 3 23 22
2015-02-02 10 60 290 280
2015-02-03 10 100 3000 250
列C
是针对2015-01-31
通过取D
的值而得出的
然后我需要使用2015-01-31
的值
,乘以2015-02-01
上A
的值
,然后添加B
我尝试了apply
和shift
使用if-else
,这会给出一个键错误。首先,创建派生值:
df.loc[0, 'C'] = df.loc[0, 'D']
然后遍历其余行并填充计算值:
for i in range(1, len(df)):
df.loc[i, 'C'] = df.loc[i-1, 'C'] * df.loc[i, 'A'] + df.loc[i, 'B']
Index_Date A B C D
0 2015-01-31 10 10 10 10
1 2015-02-01 2 3 23 22
2 2015-02-02 10 60 290 280
在numpy数组上应用递归函数将比当前答案更快
df = pd.DataFrame(np.repeat(np.arange(2, 6),3).reshape(4,3), columns=['A', 'B', 'D'])
new = [df.D.values[0]]
for i in range(1, len(df.index)):
new.append(new[i-1]*df.A.values[i]+df.B.values[i])
df['C'] = new
输出
A B D C
0 1 1 1 1
1 2 2 2 4
2 3 3 3 15
3 4 4 4 64
4 5 5 5 325
给定一列数字:
lst = []
cols = ['A']
for a in range(100, 105):
lst.append([a])
df = pd.DataFrame(lst, columns=cols, index=range(5))
df
A
0 100
1 101
2 102
3 103
4 104
可以使用shift键引用上一行:
df['Change'] = df.A - df.A.shift(1)
df
A Change
0 100 NaN
1 101 1.0
2 102 1.0
3 103 1.0
4 104 1.0
虽然这个问题已经问了一段时间了,但我会发布我的答案,希望它能帮助一些人
免责声明:我知道此解决方案不是标准解决方案,但我认为它运行良好
import pandas as pd
import numpy as np
data = np.array([[10, 2, 10, 10],
[10, 3, 60, 100],
[np.nan] * 4,
[10, 22, 280, 250]]).T
idx = pd.date_range('20150131', end='20150203')
df = pd.DataFrame(data=data, columns=list('ABCD'), index=idx)
df
A B C D
=================================
2015-01-31 10 10 NaN 10
2015-02-01 2 3 NaN 22
2015-02-02 10 60 NaN 280
2015-02-03 10 100 NaN 250
def calculate(mul, add):
global value
value = value * mul + add
return value
value = df.loc['2015-01-31', 'D']
df.loc['2015-01-31', 'C'] = value
df.loc['2015-02-01':, 'C'] = df.loc['2015-02-01':].apply(lambda row: calculate(*row[['A', 'B']]), axis=1)
df
A B C D
=================================
2015-01-31 10 10 10 10
2015-02-01 2 3 23 22
2015-02-02 10 60 290 280
2015-02-03 10 100 3000 250
所以基本上我们使用一个来自pandas的变量,并借助一个全局变量来跟踪先前的计算值
与循环的时间比较:
data = np.random.random(size=(1000, 4))
idx = pd.date_range('20150131', end='20171026')
df = pd.DataFrame(data=data, columns=list('ABCD'), index=idx)
df.C = np.nan
df.loc['2015-01-31', 'C'] = df.loc['2015-01-31', 'D']
%%timeit
for i in df.loc['2015-02-01':].index.date:
df.loc[i, 'C'] = df.loc[(i - pd.DateOffset(days=1)).date(), 'C'] * df.loc[i, 'A'] + df.loc[i, 'B']
from numba import jit
@jit(nopython=True)
def calculator_nb(a, b, d):
res = np.empty(d.shape)
res[0] = d[0]
for i in range(1, res.shape[0]):
res[i] = res[i-1] * a[i] + b[i]
return res
df['C'] = calculator_nb(*df[list('ABD')].values.T)
n = 10**5
df = pd.concat([df]*n, ignore_index=True)
# benchmarking on Python 3.6.0, Pandas 0.19.2, NumPy 1.11.3, Numba 0.30.1
# calculator() is same as calculator_nb() but without @jit decorator
%timeit calculator_nb(*df[list('ABD')].values.T) # 14.1 ms per loop
%timeit calculator(*df[list('ABD')].values.T) # 444 ms per loop
3.2 s±114 ms/圈(7次运行的平均值±标准偏差,每次1圈)
1.82 s±64.4 ms/圈(7次运行的平均值±标准偏差,每次1圈)
因此,平均速度提高了0.57倍
对于不可矢量化的递归计算,numba
,它使用JIT编译并与较低级别的对象一起工作,通常会产生很大的性能改进。您只需为
循环定义一个常规的,并使用decorator@njit
或(对于旧版本)@jit(nopython=True)
:
对于大小合理的数据帧,与常规的For
循环相比,这将使性能提高约30倍:
data = np.random.random(size=(1000, 4))
idx = pd.date_range('20150131', end='20171026')
df = pd.DataFrame(data=data, columns=list('ABCD'), index=idx)
df.C = np.nan
df.loc['2015-01-31', 'C'] = df.loc['2015-01-31', 'D']
%%timeit
for i in df.loc['2015-02-01':].index.date:
df.loc[i, 'C'] = df.loc[(i - pd.DateOffset(days=1)).date(), 'C'] * df.loc[i, 'A'] + df.loc[i, 'B']
from numba import jit
@jit(nopython=True)
def calculator_nb(a, b, d):
res = np.empty(d.shape)
res[0] = d[0]
for i in range(1, res.shape[0]):
res[i] = res[i-1] * a[i] + b[i]
return res
df['C'] = calculator_nb(*df[list('ABD')].values.T)
n = 10**5
df = pd.concat([df]*n, ignore_index=True)
# benchmarking on Python 3.6.0, Pandas 0.19.2, NumPy 1.11.3, Numba 0.30.1
# calculator() is same as calculator_nb() but without @jit decorator
%timeit calculator_nb(*df[list('ABD')].values.T) # 14.1 ms per loop
%timeit calculator(*df[list('ABD')].values.T) # 444 ms per loop
通常,避免显式循环的关键是在rowindex-1==rowindex上连接(合并)dataframe的2个实例
然后会有一个包含r和r-1行的大数据帧,从中可以执行df.apply()函数
但是,创建大型数据集的开销可能会抵消并行处理的好处…这是一个老问题,但下面的解决方案(没有for循环)可能会有所帮助:
def new_fun(df):
prev_value = df.iloc[0]["C"]
def func2(row):
# non local variable ==> will use pre_value from the new_fun function
nonlocal prev_value
new_value = prev_value * row['A'] + row['B']
prev_value = row['C']
return new_value
# This line might throw a SettingWithCopyWarning warning
df.iloc[1:]["C"] = df.iloc[1:].apply(func2, axis=1)
return df
df = new_fun(df)
为什么数据帧中的最后一行与列A
和B
不同?@Anton现在对此表示歉意。列A
和列D
中下一行的值是什么?这是一个好问题。我同样需要一个矢量化的解决方案。如果提供了apply()
版本,用户的函数能够访问上一行中的一个或多个值作为其计算的一部分,或者至少返回一个值,然后在下一次迭代中“传递给自身”,那就太好了。与for循环相比,这难道不会带来一些效率提高吗?@Bill,你可能对我刚才添加的内容感兴趣,numba
通常是一个很好的选择。pandas中有没有一个函数可以在没有循环的情况下实现这一点?计算的迭代性质,其中输入取决于前面步骤的结果,这使得矢量化变得复杂。您可以将apply
与执行与循环相同计算的函数一起使用,但在幕后这也是一个循环。如果我使用这个循环并对合并的数据帧进行计算,它会找到一个Nan,但它只对带有Nan的行起作用。如果我尝试使用fillNa,则不会引发任何错误。我会得到AttributeError:'numpy.float64'对象没有属性'fillNa'是否有任何方法可以跳过带有Nan的行或将值设置为零?您的意思是在C
以外的列中缺少值?是的,您的解决方案很好。我只是确保在循环之前在数据帧中填充NAN。这个答案对于我来说非常适用于类似的计算。我尝试使用cumsum和shift的组合,但这个解决方案效果更好。谢谢。这对我来说也很好,谢谢。我一直在努力解决许多形式的iTerrow、itertuples、apply等问题,这似乎很容易理解和实现。在这种情况下,这不会有帮助,因为前一行的值在开始时是未知的。每次迭代都要计算它,然后在下一次迭代中使用。我仍然很感激这个答案,因为我偶然发现了这个问题,正在寻找一个我确实知道上一行值的例子。所以谢谢@kztdIt太棒了!我加速了我的功能,它计算值从以前的值。谢谢如何在jupyter笔记本中使用@jit(nopyton=True)
?@sergzemsk,正如您所写的(在我的回答中),它被称为一个。注意:numba的更高版本支持快捷方式@njit
@jpp,我有if
条件,因此此改进失败。我收到一个错误“TypingError:在nopython模式管道中失败(步骤:nopython frontend)”@sergzemsk,我建议你问一个新问题,我不清楚if
语句的位置,为什么它没有被numba矢量化。这使得一些关于.apply
的假设可能不成立:如果.apply
被并行化或调用的顺序不同于您预期的顺序,结果将不符合预期。我同意您的担忧。本anwser中的假设基于此线程的问题。此外,默认情况下应用程序不会并行化。。。