【问题标题】:replace specific value in pandas dataframes using the mean between 10 previous and next values使用 10 个前一个值和下一个值之间的平均值替换 pandas 数据帧中的特定值
【发布时间】:2021-12-10 11:39:16
【问题描述】:

假设我有以下数据框

df.Consumption

0        16.208
1        11.193
2         9.845
3         9.348
4         9.091
          ...  
19611     0.000
19612     0.000
19613     0.000
19614     0.000
19615     0.000
Name: Consumption, Length: 19616, dtype: float64

我想用不是 0.00 的前 10 个值和下一个值的平均值替换 0 值

有什么好的方法吗?我正在考虑使用 replace 和 interpolate 方法,但我看不到如何有效地编写它

【问题讨论】:

  • 上一个值和下一个值的个数是如何分布的?对于非零的前一个和下一个数字,我们应该各有 5 个,还是应该有最接近的前一个和下一个非零值,前一个优先级?
  • @taxevader 是的,这将是最合乎逻辑的方式。我也认为它应该是平均分布的,如果没有足够的值(例如,如果我们在开始或结束时我们从另一边选择更多),但无论如何我猜这是一个细节。我只是想看看是否有不需要循环的方法

标签: python pandas dataframe imputation


【解决方案1】:

您可以使用Series.rolling()center=True 以及Rolling.mean() 来获得上一个和下一个值的平均值。

如果您想从平均值计算中排除 0,请将 0 替换为 NaN

设置center=True 以便滚动窗口同时查找上一个和下一个条目。

最后,使用.loc 将值为0 的条目设置为平均值,如下所示:

n = 10     # check previous and next 10 entries

# rolling window size is (2n + 1)
Consumption_mean = (df['Consumption'].replace(0, np.nan)
                                     .rolling(n * 2 + 1, min_periods=1, center=True)
                                     .mean())

df.loc[df['Consumption'] == 0, 'Consumption'] = Consumption_mean

演示

使用较小的窗口尺寸n = 3进行演示:

df


    Consumption
0        16.208
1        11.193
2         9.845
3         9.348
4         9.091
5         8.010
6         0.000              <====   target entry
7         7.100
8         0.000              <====   target entry
9         6.800
10        6.500
11        6.300
12        5.900
13        5.800
14        5.600

#n = 10     # check previous and next 10 entries
n = 3     # smaller window size for demo

# rolling window size is (2n + 1)
Consumption_mean = (df['Consumption'].replace(0, np.nan)
                                     .rolling(n * 2 + 1, min_periods=1, center=True)
                                     .mean())

# Update into a new column `Consumption_New` for demo purpose
df['Consumption_New'] = df['Consumption']    
df.loc[df['Consumption'] == 0, 'Consumption_New'] = Consumption_mean

演示结果:

print(df)

    Consumption  Consumption_New
0        16.208          16.2080
1        11.193          11.1930
2         9.845           9.8450
3         9.348           9.3480
4         9.091           9.0910
5         8.010           8.0100
6         0.000           8.0698   # 8.0698 = (9.348 + 9.091 + 8.01 + 7.1 + 6.8) / 5 with skipping 0.000 between 7.100 and 6.800
7         7.100           7.1000
8         0.000           6.9420   # 6.942 = (8.01 + 7.1 + 6.8 + 6.5 + 6.3) / 5 with skipping 0.000 between 8.010 and 7.100
9         6.800           6.8000
10        6.500           6.5000
11        6.300           6.3000
12        5.900           5.9000
13        5.800           5.8000
14        5.600           5.6000

【讨论】:

  • 感谢您的回答。我将验证它作为已回答的解决方案。但是,我有一个问题。假设我有 10 个 nans,如果我选择 7 个窗口,那么中间的窗口将只有 nans 的平均值。有没有办法只计算非 Nans 值的行数?
  • @lalaland 实际上,如果您只想计算非 Nans 值的行,最明确的方法是在处理之前删除带有 NaN 的行。例如在处理之前使用dropna() 删除NaN 行。
  • 但结果数据框的大小不会相同,我无法用计算的平均值替换它,不是吗?
  • @lalaland 您可以在此处的所有步骤之前删除Nan 行,以便均值计算和替换步骤基于相同的已删除数据框,以便数据一致且正确对齐。
  • 是的,我的意思是第二个选项。那么有没有办法动态改变窗口的大小呢?谢谢回答。我很感激
【解决方案2】:

这应该让您非常接近。它利用了不计入平均值的空值,因此您可以用 nan 替换零,然后循环遍历。

如果没有逐行应用,我不确定是否有更好的方法。

有些事情告诉我,做一个实际的循环,每次迭代更新 df 会给你带来稍微不同的结果,因为你会在你去的时候输入空值,这将使前 10 个结果总是有一个值。

import pandas as pd
df = pd.DataFrame({'Consumption':[1,1,1,1,1,1,1,1,1,0,2,2,2,2,2,2,2,2,2,2]})
df.replace(0,np.nan, inplace=True)
df.update(df.apply(lambda x:np.mean(df.Consumption.iloc[max(x.name-10,0):]), axis=1).to_frame('Consumption'),overwrite=False)

输出

Consumption
0   1.000000
1   1.000000
2   1.000000
3   1.000000
4   1.000000
5   1.000000
6   1.000000
7   1.000000
8   1.000000
9   1.526316
10  2.000000
11  2.000000
12  2.000000
13  2.000000
14  2.000000
15  2.000000
16  2.000000
17  2.000000
18  2.000000
19  2.000000

【讨论】:

  • 谢谢@Chris,这很有帮助:)
猜你喜欢
  • 2023-03-23
  • 2014-09-04
  • 2019-10-07
  • 2016-10-03
  • 2016-07-24
  • 2020-05-15
  • 2022-09-23
  • 2019-08-06
  • 1970-01-01
相关资源
最近更新 更多