假设我有一个像这样的DataFrame(或系列):
f
我希望创建一个新的结果列。
每个结果的值由之前的值,通过任意函数f
确定。
如果以前的值不可用(无或NaN),我希望使用上一个结果(当然也可以使用shift
)。
使用之前的值很简单,我只需要使用df['Result'] = df['Value'].shift(1).apply(f)
。但是,访问之前的结果似乎并不那么简单。
例如,以下代码计算结果,但如果需要,则无法访问以前的结果。
f
请假设cumsum
是任意的,因此无法使用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)
之类的解决方案。
显然,这可以通过迭代完成,但我想知道是否存在更多Panda-y解决方案。
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}}
答案 0 :(得分:3)
看起来它必须是我的循环。我厌恶循环...所以当我循环时,我使用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 :(得分:0)
我认为这可行,但我不确定。它将先前计算的值存储在闭包中。
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))
答案 2 :(得分:0)
我建议没有显式循环的解决方案。而不是引用之前的值,而是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
结果:
typedef boost::variant<int, float, std::string > Variant;
using Func = std::function<std::vector<unsigned char>(std::vector<Variant>)>;
void addexecutorfunc( Func callback, const auto&...args )
{
std::vector<Variant> vec = {args...};
executor.add(vec, std::move(callback));
}
答案 3 :(得分:-1)
这可能适合熊猫编码风格。但是,效率方面,我认为需要进一步测试。这不适用于一般功能。这个 不知何故欺骗了加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))