当前行值也在 application 中计算时,熊猫有没有办法使用 dataframe. application 中的前行值?

我有以下数据框:

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

Column C通过取 Dvalue得到 2015-01-31

然后,我需要使用 Cvalue2015-01-31和乘以 2015-02-01上的 Avalue和加上 B

我已经尝试了一个 apply和一个 shift使用一个 if else,这给出了一个关键错误。

234873 次浏览

首先,创建派生值:

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

在数字数组上应用递归函数将比当前的答案更快。

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

Df [‘ Change’] = df.A-df.A.shift (1,fill _ value = df.A [0]) # 填充缺少的值,例如100
Df

    A   Change
0   100 0.0
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

所以基本上我们使用一个来自大熊猫的 apply和一个全局变量的帮助,这个全局变量跟踪以前的计算值。


for循环的时间比较:

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']

每个循环3.2 s ± 114ms (平均值 ± 标准开发值7次,每次循环1次)

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


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


%%timeit
df.loc['2015-02-01':, 'C'] = df.loc['2015-02-01':].apply(lambda row: calculate(*row[['A', 'B']]), axis=1)

每个循环1.82 s ± 64.4 ms (平均值 ± std. dev. 7次,每次1个循环)

所以平均快0.57倍。

numba

对于不能向量化的递归计算,使用 JIT 编译并使用较低级别对象的 numba通常会带来很大的性能改进。您只需定义一个常规的 for循环,并使用装饰器 @njit或(对于旧版本) @jit(nopython=True):

对于一个合理大小的数据帧,与常规的 for循环相比,性能提高了约30倍:

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 上联接(merge)数据框架的2个实例。

然后就会有一个包含 r 和 r-1行的大型数据框架,从这里可以执行 df.application ()函数。

然而,创建大型数据集的开销可能会抵消并行处理的好处..。

这是一个老问题,但下面的解决方案(没有 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)