Pandas 条件填充 NaN 向前/向后

数据挖掘 Python 熊猫 数据清理
2021-10-06 13:13:04

2018 年 10 月 22 日更新: 我有以下数据集:

data = [('D',1,10,8),
           ('D',2,12,12),
           ('X',1,28,np.NaN),
           ('D',3,np.NaN,np.NaN),
           ('X',2,np.NaN,25),
           ('X',3,32,25),
           ('T',1,220,np.NaN),
           ('X',4,30,np.NaN),
           ('T',2,240,np.NaN),
           ('X',2,38,np.NaN),
           ('T',3,np.NaN,np.NaN),
           ('T',4,200,150)]

labels = ['item', 'month','normal_price','final_price']

df = pd.DataFrame.from_records(data, columns=labels)

    item    month   normal_price    final_price
0   D       1       10.0            8.0
1   D       2       12.0            12.0
2   X       1       28.0            NaN
3   D       3       NaN             NaN
4   X       2       NaN             25.0
5   X       3       32.0            25.0
6   T       1       220.0           NaN
7   X       4       30.0            NaN
8   T       2       240.0           NaN
9   X       2       38.0            NaN
10  T       3       NaN             NaN
11  T       4       200.0           150.0

我想用前一个月的 , 填写每个项目NaN'normal_price',列(如果在下个月不可用)。我试过用这个:'final_price''normal_price''final_price'

df[['normal_price','final_price']]=df[['normal_price','final_price']].fillna(method='ffill')

但它给了我这个:

    item    month   normal_price    final_price
0   D       1   10.0                8.0
1   D       2   12.0                12.0
2   X       1   28.0                12.0*
3   D       3   28.0*               12.0
4   X       2   28.0                25.0
5   X       3   32.0                25.0
6   T       1   220.0               25.0*
7   X       4   30.0                25.0
8   T       2   240.0               25.0*
9   X       2   38.0                25.0
10  T       3   38.0*               25.0*
11  T       4   200.0               150.0

问题在于带有星号的案例(我也尝试过'bfill')。这些值应根据其正确的项目填写。理想情况下,我应该得到:

    item    month   normal_price    final_price
0   D       1   10.0                8.0
1   D       2   12.0                12.0
2   X       1   28.0                25.0
3   D       3   12.0                12.0
4   X       2   28.0                25.0
5   X       3   32.0                25.0
6   T       1   220.0               150.0
7   X       4   30.0                25.0
8   T       2   240.0               150.0
9   X       2   38.0                25.0
10  T       3   220.0               150.0
11  T       4   200.0               150.0

我还尝试了以下方法(根据日期提供的答案):

df[['normal_price','final_price']].ffill(limit=1).bfill(limit=1)

或者

df[['normal_price','final_price']]=df[['normal_price','final_price']].interpolate(method='nearest')

但是他们都没有给我与每个项目相对应的合理填充。我找到了这个方法:

df[['normal_price','final_price']]=df[['normal_price','final_price']].fillna(df.groupby(['item'])[['normal_price','final_price']].transform('mean'))

它工作得更好,但它为值引入了不可预测的值(在本例中为'mean'NaN,而不是我最初想要的前面或后面的值。我试图将这个df.groupby(['item'])概念与'.ffill'or结合起来'.bfill',但到目前为止还没有成功。

2个回答

只需使用该fillna方法并提供limit关于应填充多少 NA 值的信息。您只希望填充第一个值,因此将其设置为1

df.ffill(limit=1)                                                       

  item  month  normal_price  final_price
0    1      1          10.0          8.0
1    1      2          12.0         12.0
2    1      3          12.0         12.0
3    2      1           NaN         25.0
4    2      2          30.0         25.0
5    3      3          30.0          NaN
6    3      4         200.0        150.0

您可以将上述内容与 a 链接在一起,bfill然后填充剩余的 NaN 值:

df.ffill(limit=1).bfill(limit=1)

  item  month  normal_price  final_price
0    1      1          10.0          8.0
1    1      2          12.0         12.0
2    1      3          12.0         12.0
3    2      1          30.0         25.0
4    2      2          30.0         25.0
5    3      3          30.0        150.0
6    3      4         200.0        150.0

如果您的数据框中有您希望不受影响的列,这将不是最佳选择。

在这种情况下,你可以一次做一列——我使用in_place标志,这样我们就不需要做任何丑陋的重新分配:

df.final_price.ffill(inplace=True, limit=1)                                     

df                                                                     

  item  month  normal_price  final_price
0    1      1          10.0          8.0
1    1      2          12.0         12.0
2    1      3           NaN         12.0
3    2      1           NaN         25.0
4    2      2          30.0         25.0
5    3      3           NaN          NaN
6    3      4         200.0        150.0

正如我在上面所做的那样,相同的想法将使用bfill方法而不是向后填充值。ffill

您可以使用 pandas插值函数。

df[['normal_price','final_price']]=df[['normal_price','final_price']].interpolate(method='nearest')