在 pandas 数据框中编写用户定义的 fillna 函数,以用条件填充 np.nan 不同的值

Write a user defined fillna function in pandas dataframe to fill np.nan different values with conditions

考虑以下 pandas 数据帧:

import pandas as pd

change = [0.475, 0.625, 0.1, 0.2, -0.1, -0.75, 0.1, -0.1, 0.2, -0.2]
position = [1.0, 1.0, nan, nan, nan, -1.0, nan, nan, nan, nan]
date = ['20150101', '20150102', '20150103', '20150104', '20150105', '20150106', '20150107', '20150108', '20150109', '20150110']
pd.DataFrame({'date': date, 'position': position, 'change': change})

产出

     date        change       position    
    20150101      0.475          1
    20150102      0.625          1
    20150103      0.1            np.nan
    20150104      0.2            np.nan
    20150105      -0.1           np.nan
    20150106      -0.75          -1
    20150107      0.1            np.nan
    20150108      -0.1           np.nan
    20150109      0.2            np.nan
    20150110      -0.2           np.nan

我想填写以下规则:

  1. 对于"position"值为np.nan的行,如果"change"的值与position的最后一个非空值的符号相同(change * position >0,比如0.1*1和0.2*1 >0),我们用最后一个非空值填充。

  2. 对于"position"值为np.nan的行,如果"change"的值与position的最后一个非空值的符号相同(改变* position <=0 比如-1*0.1),我们填0.

  3. 一个np.nan补0后,后面的np.nan也补0

以下是示例数据框的预期结果:

     date        change       position    
    20150101      0.475          1
    20150102      0.625          1
    20150103      0.1            1
    20150104      0.2            1
    20150105      -0.1           0
    20150106      -0.75          -1
    20150107      0.1            0
    20150108      -0.1           0
    20150109      0.2            0
    20150110      -0.2           0

编辑:

我开发的方法如下:

while(any(np.isnan(x['position']))):
    conditions = [(np.isnan(x['position'])) & (x['position'].shift(1) * x['change'] > 0),
                  (np.isnan(x['position'])) & (x['position'].shift(1) * x['change'] <= 0)]
    choices = [x['position'].shift(1), 0]
    x['position'] = np.select(conditions, choices, default=x['position'])

但是如您所见,它不是很令人满意,如果您有 80,000,000 行数据,它会非常慢。

有什么建议吗?感谢您的帮助!

我认为您的代码非常可靠,主要问题是您迭代它的次数超出了您的需要。 shift() 一次只返回一行,但是如果您将其更改为 fillna(method='ffill') 那么您基本上会得到无限次的轮班,但只需要这样做一次而不是多次迭代(多少次迭代次数将取决于您的数据)。

conditions = [
    (np.isnan(x['position'])) & (x['position'].fillna(method='ffill')*x['change']>0),
    (np.isnan(x['position'])) & (x['position'].fillna(method='ffill')*x['change']<=0)]

但我相信您可以更进一步,通过在末尾添加另一个 fillna 来消除 while

conditions = [
    (np.isnan(x['position'])) & (x['position'].fillna(method='ffill')*x['change']>0),
    (np.isnan(x['position'])) & (x['position'].fillna(method='ffill')*x['change']<=0)]

choices=[x['position'].shift(1),0]
x['position'] = np.select(conditions,choices,default=x['position'])

x['position'] = x['position'].fillna(method='ffill')

在您的示例数据上,第一个更改比您的代码快约 2 倍,第二个约快 4 倍。我得到的答案和你一样,但当然你会想在真实数据上验证这一点。