熊猫申请,但进入先前计算值

问题描述:

假设我有一个数据框(或系列)是这样的:熊猫申请,但进入先前计算值

 Value 
0 0.5 
1 0.8 
2 -0.2 
3 None 
4 None 
5 None 

我希望创建一个新的结果列。

每个结果的值由以前的值通过任意函数f确定。

如果以前的值不可用(无或NaN),我希望使用以前的结果(并将其应用f当然)。


使用以前的值很容易,我只需要使用shift。但是,访问以前的结果似乎并不那么简单。

例如,下面的代码计算结果,但如果需要则无法访问以前的结果。

df['Result'] = df['Value'].shift(1).apply(f) 

请假设f是任意的,因而使用像cumsum解决方案是不可能的。


很明显,这可以通过迭代来完成,但我想知道是否存在更多Panda-y解决方案。

df['Result'] = None 
for i in range(1, len(df)): 
    value = df.iloc[i-1, 'Value'] 
    if math.isnan(value) or value is None: 
    value = df.iloc[i-1, 'Result'] 
    df.iloc[i, 'Result'] = f(value) 

输出示例,给出f = lambda x: x+1

坏:

Value Result 
0 0.5  NaN 
1 0.8  1.5 
2 -0.2  1.8 
3 NaN  0.8 
4 NaN  NaN 
5 NaN  NaN 

好:

Value Result 
0 0.5  NaN 
1 0.8  1.5 
2 -0.2  1.8 
3 NaN  0.8 
4 NaN  1.8 <-- previous Value not available, used f(previous result) 
5 NaN  2.8 <-- same 
+1

如何'DF [ '结果'] = DF [ '值']。移(1)。适用(F).ffill()'? – IanS

+1

@IanS由'ffill'填充的值不会对其应用'f'。 – gberger

+0

编辑机构澄清上述 – gberger

看起来它必须是一个循环我。我厌恶循环...所以当我循环时,我使用numba

Numba为您提供了使用Python直接编写的高性能函数加速应用程序的能力。通过一些注释,面向数组和面向数学的Python代码可以即时编译为本地机器指令,与C,C++和Fortran的性能类似,无需切换语言或Python解释器。

https://numba.pydata.org/

from numba import njit 


@njit 
def f(x): 
    return x + 1 

@njit 
def g(a): 
    r = [np.nan] 
    for v in a[:-1]: 
     if np.isnan(v): 
      r.append(f(r[-1])) 
     else: 
      r.append(f(v)) 
    return r 

df.assign(Result=g(df.Value.values)) 

    Value Result 
0 0.5  NaN 
1 0.8  1.5 
2 -0.2  1.8 
3 NaN  0.8 
4 NaN  1.8 
5 NaN  2.8 

这可能适合大熊猫的编码风格。但是,从效率角度来看,我认为还需要进一步的测试。这不适用于一般功能。这个 以某种方式欺骗了加1功能。

import pandas as pd 
import numpy as np 


df = pd.DataFrame({'Value':[0.5,0.8,-0.2,None,None,None]}) 
index = df['Value'].index[df['Value'].apply(np.isnan)] 
window = max(index)-min(index)+1 
df['next'] =df['Value'].shift(1) 


def getX(x): 
    last = np.where(~np.isnan(x))[0][-1] 
    return (x[last])+len(x)-last 



df['plus_one'] = df['next'].rolling(window=3,min_periods=1).apply(lambda x: getX(x)) 
+0

这不起作用,因为'f'是一个任意的函数,例如它可能是'math.sin',或其他任何东西。 – gberger

我认为这可能有效,但我不确定。它将先前计算的值存储在闭包中。

def use_previous_if_none(f): 
    prev = None 
    def wrapped(val): 
     nonlocal prev 
     if math.isnan(val) or val is None: 
       val = prev 
     res = f(val) 
     prev = res 
     return res 
    return wrapped 

df['Result'] = df.Value.shift(1).apply(use_previous_if_none(f)) 
+0

@piRSquared的想法? – gberger

我建议一个没有显式循环的解决方案。它不是引用上一个值,而是ffil()'s,NaNs,然后根据需要应用f所需的时间,仅限于NaNs的值。

我们首先来定义帮助功能,将调用fn时间:

def apply_f_n_times(arg): 
    x = arg[0] 
    n = int(arg[1]) 
    for i in range(n): 
     x = f(x) 
    return x 

df = pd.DataFrame({'value': [1, 2, 3, 5, None, None, 12, 9, None, 6, 1, None, None, None]}) 
df['Result'] = df['Value'].shift(1).apply(f) 
# the following 2 lines will create counter of consecutive NaNs 
tmp = df['Result'].isnull() 
df['Apply_times'] = tmp * (tmp.groupby((tmp != tmp.shift()).cumsum()).cumcount() + 1) 
# fill NaNs with previous good value 
df['Result'] = df['Result'].ffill() 
# apply N times 
df['Result'] = df[['Result', 'Apply_times']].apply(apply_f_n_times, axis=1) 

结果:

Out[2]: 
     Value Result Apply_times 
0  1.0  nan   1 
1  2.0  2.0   0 
2  3.0  3.0   0 
3  5.0  4.0   0 
4  nan  6.0   0 
5  nan  7.0   1 
6 12.0  8.0   2 
7  9.0 13.0   0 
8  nan 10.0   0 
9  6.0 11.0   1 
10 1.0  7.0   0 
11 nan  2.0   0 
12 nan  3.0   1 
13 nan  4.0   2