用中位数替换NaN值?

3

我想使用Pandas来替换表中特定范围内的所有NaN值为其中位数(median)值。虽然我在处理更大的数据集,但是以下是一个例子:

np.random.seed(0)
rng = pd.date_range('2020-09-24', periods=20, freq='0.2H')
df = pd.DataFrame({ 'Date': rng, 'Val': np.random.randn(len(rng)), 'Dist' :np.random.randn(len(rng)) }) 
df.Dist[df.Dist<=-0.6] = np.nan
df.Val[df.Val<=-0.5] = np.nan

我希望能够将Val和Dist中的NaN值替换为每小时该列的中位数。我已经在另一个参考表中获取了中位数值:

Data

df.set_index('Date', inplace=True)
df = df.assign(Hour = lambda x : x.index.hour)
df_val = df[["Val", "Hour"]].groupby("Hour").median()
df_dist = df[["Dist", "Hour"]].groupby("Hour").median()

中位数值

但现在我已经尝试了下面的所有命令,无论怎样都无法弥补 NaN 值。

df[["Val","Hour"]].mask(df['Val'].isna(), df_val.iloc[df.Hour], inplace=True)

df.where(df['Val'].notna(), other=df_val[df.Hour],axis = 0)

df["Val"] = np.where(df['Val'].notna(), df['Val'], df_val(df.Hour))

df.replace({"Val":{np.nan:df_val[df.Hour]}, "Dist":{np.nan:df_dist[df.Hour]}})
4个回答

3
你可以使用 groupby.transform 和 fillna:
cols = ['Val','Dist']
df[cols] =  df[cols].fillna(df.groupby(df.Date.dt.floor('H'))
                              [cols].transform('median')
                           )

输出:

                  Date       Val      Dist
0  2020-09-24 00:00:00  1.764052  0.864436
1  2020-09-24 00:12:00  0.400157  0.653619
2  2020-09-24 00:24:00  0.978738  0.864436
3  2020-09-24 00:36:00  2.240893  0.864436
4  2020-09-24 00:48:00  1.867558  2.269755
5  2020-09-24 01:00:00  0.153690  0.757559
6  2020-09-24 01:12:00  0.950088  0.045759
7  2020-09-24 01:24:00 -0.151357 -0.187184
8  2020-09-24 01:36:00 -0.103219  1.532779
9  2020-09-24 01:48:00  0.410599  1.469359
10 2020-09-24 02:00:00  0.144044  0.154947
11 2020-09-24 02:12:00  1.454274  0.378163
12 2020-09-24 02:24:00  0.761038  0.154947
13 2020-09-24 02:36:00  0.121675  0.154947
14 2020-09-24 02:48:00  0.443863 -0.347912
15 2020-09-24 03:00:00  0.333674  0.156349
16 2020-09-24 03:12:00  1.494079  1.230291
17 2020-09-24 03:24:00 -0.205158  1.202380
18 2020-09-24 03:36:00  0.313068 -0.387327
19 2020-09-24 03:48:00  0.323371 -0.302303

非常简洁的解决方案,您能否解释/链接详细说明如何使用groupby()transform()之间只有一个间隔? - EddyWD
另外,为什么使用.transform('median')比使用.median更好? - EddyWD
1
.median 每组只返回一个值,因此您将获得一个长度等于组数的数据框/系列。 transform 重新填充组之间的值,因此您将收到与原始数据框具有相同索引的数据框/系列。由于您正在将其分配回原始数据框,因此 transform 的效果更好。 - Quang Hoang

1

您可以使用groupby -> transform操作,同时利用pd.Grouper类执行按小时转换。这将创建一个与原始数据具有相同形状的数据帧,并计算每小时的中位数。一旦完成此操作,您可以直接使用DataFrame.fillna

hourly_medians = df.groupby(pd.Grouper(key="Date", freq="H")).transform("median")
out = df.fillna(hourly_medians)

print(out)

                  Date       Val      Dist
0  2020-09-24 00:00:00  1.764052  0.864436
1  2020-09-24 00:12:00  0.400157  0.653619
2  2020-09-24 00:24:00  0.978738  0.864436
3  2020-09-24 00:36:00  2.240893  0.864436
4  2020-09-24 00:48:00  1.867558  2.269755
5  2020-09-24 01:00:00  0.153690  0.757559
6  2020-09-24 01:12:00  0.950088  0.045759
7  2020-09-24 01:24:00 -0.151357 -0.187184
8  2020-09-24 01:36:00 -0.103219  1.532779
9  2020-09-24 01:48:00  0.410599  1.469359
10 2020-09-24 02:00:00  0.144044  0.154947
11 2020-09-24 02:12:00  1.454274  0.378163
12 2020-09-24 02:24:00  0.761038  0.154947
13 2020-09-24 02:36:00  0.121675  0.154947
14 2020-09-24 02:48:00  0.443863 -0.347912
15 2020-09-24 03:00:00  0.333674  0.156349
16 2020-09-24 03:12:00  1.494079  1.230291
17 2020-09-24 03:24:00 -0.205158  1.202380
18 2020-09-24 03:36:00  0.313068 -0.387327
19 2020-09-24 03:48:00  0.323371 -0.302303

0
您可以为所需的任务定义一个函数:
    def impute_nan(df,var,median):
        df['new_'+var] = df[var].fillna(median)
    median = df.Val.medain()
    median
    impute_nan(df,'Val',median)

这将为您提供一个名为'new_Val'的新列,其中包含替换了NAN值的数据。


0

根据你所做的,我会这样做:

df.Val = df.Val.fillna(df.Hour.map(df_val.squeeze()))
df.Dist = df.Val.fillna(df.Hour.map(df_dist.squeeze()))

如果我先压缩中位数值,那么我的一些 where()replace() 是否会起作用呢? - EddyWD
不这么想,因为在 df_val[df.Hour] 这里,你正在将整个列的值传递给 df_val,这应该会引发错误。 - Juan C
".fillna() 期望得到标量、字典或者Series,而 .map() 并不会传递这些参数,因此似乎无法正常工作。" - EddyWD
df.Hour.map(df_dist.squeeze()) 是一个序列,所以它实际上是有效的。 - Juan C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接