Pandas中是否有方法在dataframe.apply中使用前一行值,而前一行值也在apply中计算?

wwodge7n  于 2023-06-20  发布在  其他
关注(0)|答案(7)|浏览(129)

我有以下dataframe:

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-01Avalue,然后加上B
我已经尝试了一个apply和一个shift使用一个if else通过这给出了一个关键错误。

jhkqcmku

jhkqcmku1#

首先,创建派生值:

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
taor4pac

taor4pac2#

给定一列数字:

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

您可以使用fill_value参数填充缺少的值

df['Change'] = df.A - df.A.shift(1, fill_value=df.A[0]) # fills in the missing value e.g. 100<br>
df

    A   Change
0   100 0.0
1   101 1.0
2   102 1.0
3   103 1.0
4   104 1.0
zhte4eai

zhte4eai3#

numba

对于不可向量化的递归计算,numba使用JIT编译并与较低级别的对象一起工作,通常会产生很大的性能改进。你只需要定义一个常规的for循环,并使用装饰器@njit或(对于旧版本)@jit(nopython=True)
对于一个合理大小的 Dataframe ,与常规的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
vecaoik1

vecaoik14#

在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
ruoxqz4g

ruoxqz4g5#

虽然这个问题已经有一段时间没有被问到了,但我还是会把我的答案贴出来,希望能对大家有所帮助。

    • 免责声明:**我知道这个解决方案不是 * 标准 *,但我认为它工作得很好。
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中的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.2s ± 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/循环(平均值±标准值)7次运行的平均值,每次1个循环)**

平均速度是0.57倍。

hmae6n7t

hmae6n7t6#

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

vkc1a9a27#

一般来说,避免显式循环的关键是在rowindex-1==rowindex上连接(合并)2个 Dataframe 示例。
然后,您将有一个包含r和r-1行的大 Dataframe ,您可以从其中执行df.apply()函数。
然而,创建大型数据集的开销可能会抵消并行处理的好处。

相关问题