首页 > 解决方案 > 用下一个和上一个正数的平均值替换负数、NaN 和 0

问题描述

我想用同一列的下一个和上一个正数的平均值替换负数、NaN 和 0。

原始数据框

    a   c
0   1   1
1   2   2
2   0   5
3   -3  NaN
4   -1  5
5   3   3

预期的输出数据帧是

    a    c
0   1     1
1   2     2
2   2.5   5    #In Col a --> Mean of 2 and 3 is 2.5 hence 0 replaced by 2.5
3   2.75  5  #In Col a --> Mean of 2.5 and 3 is 2.75 hence negative no. replaced by 2.75
4   2.875 5    #In Col a --> Mean of 2.75 and 3 is 2.875 hence negative no. replaced by 2.875
5   3     3

我尝试了另一种策略来处理否定的否。Nan 和 0 用前 3 个值的平均值替换它

m = df['a'] < 1
new = (df.loc[~m, 'a'].astype(float)
         .rolling(2, min_periods=1).mean()
         .reindex(df.index, method='ffill'))

df['a'].mask(m, new)

这导致

0    1.0
1    2.0
2    1.5
3    1.5
4    1.5
5    2.0
Name: a, dtype: float64

但是,我正在努力实施新战略(被问到)。

标签: pythonpython-3.xpandasreplacetime-series

解决方案


我编辑了我的答案以更好地解决您的问题。但是请注意,5 和 5 的平均值是 5,而不是您在预期结果中写的 2.5。

这个新答案基于下面 hpchavaz 的答案。

# Replace 0 and negative values with NaN
df = df.mask(df<=0)

# Compute rank of consecutive NaN values
rank = df.isnull().astype('int')
rank = rank.cumsum() - rank.cumsum().where(rank==0).ffill().fillna(0)
print(rank)

     a    b
0  0.0  0.0
1  0.0  0.0
2  1.0  0.0
3  2.0  1.0
4  3.0  0.0
5  0.0  0.0

# Compute first and last non null value before NaN range
first = df.ffill()
last = df.bfill()

# Finally, compute final df
df = last - (last-first)/2**(rank)
print(df)

       a    b
0  1.000  1.0
1  2.000  2.0
2  2.500  5.0
3  2.750  5.0
4  2.875  5.0
5  3.000  3.0

上一个答案

您可以调用mask用 NaN 替换空值和负值,然后interpolate

不太确定为什么您希望第二列中的 NaN 被替换为 2.5 而不是 5 ......

>>> df.mask(df<=0).interpolate()
      a    b
0  1.00  1.0
1  2.00  2.0
2  2.25  5.0
3  2.50  5.0
4  2.75  5.0
5  3.00  3.0

推荐阅读