熊猫申请,但进入先前计算值
假设我有一个数据框(或系列)是这样的:熊猫申请,但进入先前计算值
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
看起来它必须是一个循环我。我厌恶循环...所以当我循环时,我使用numba
Numba为您提供了使用Python直接编写的高性能函数加速应用程序的能力。通过一些注释,面向数组和面向数学的Python代码可以即时编译为本地机器指令,与C,C++和Fortran的性能类似,无需切换语言或Python解释器。
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))
这不起作用,因为'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))
@piRSquared的想法? – gberger
我建议一个没有显式循环的解决方案。它不是引用上一个值,而是ffil()'s
,NaNs
,然后根据需要应用f
所需的时间,仅限于NaNs
的值。
我们首先来定义帮助功能,将调用f
n
时间:
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
如何'DF [ '结果'] = DF [ '值']。移(1)。适用(F).ffill()'? – IanS
@IanS由'ffill'填充的值不会对其应用'f'。 – gberger
编辑机构澄清上述 – gberger