我正试图找到一个漂亮/聪明的用组的中值填充我的DataFrame的方法。
我有两个组"我"one_answers";J"和2个因素';a &;和";B"。我想用这个值所属群的中位数来代替负值。
一个约束是我不想替换NaN值。下面是初始化DataFrame
tuples = [('I','0'), ('I','1'), ('I', '2'), ('J', '3'), ('I', '4'), ('J', '5')]
index = pd.MultiIndex.from_tuples(tuples, names=["Id1", "Id2"])
df = pd.DataFrame(np.arange(12).reshape(-1, 2), columns=['A', 'B'], index=index)
df["A"].iloc[0]=-1
df["B"].iloc[-1:]=-1
df["B"].iloc[-2]=18
df["B"].iloc[0]=np.NaN
df["B"].iloc[1]=np.NaN
给了:
A B
Id1 Id2
I 0 -1 NaN
1 2 NaN
2 4 5.0
J 3 6 7.0
I 4 8 18.0
J 5 10 -1.0
我是这样解决的:
ind, col = np.where(df<0)
nb_df_lt_0 = len(ind)
for ii in np.arange(nb_df_lt_0) :
df.iloc[ind[ii],col[ii]] = np.NAN
xx, yy = ind[ii], col[ii]
index_Id1 = df.index.get_level_values("Id1")[xx]
df.iloc[xx,yy] = df.loc[index_Id1,:].iloc[:,yy].median()
df
这就是我要找的:
A B
Id1 Id2
I 0 4.0 NaN
1 2.0 NaN
2 4.0 5.0
J 3 6.0 7.0
I 4 8.0 18.0
J 5 10.0 7.0
它可以工作,但它看起来不太好,而且肯定不是很有效,因为我有一个For
循环。我会很高兴看到一个解决方案与熊猫或numpy功能,使工作。
提前致谢
你可以这样做:
df.mask(df<0, df.mask(df<0, np.nan).groupby(level=0).median())
让我们来分析一下。你需要两组的中位数和";J"排除负值:
median_df = df.mask(df<0, np.nan).groupby(level=0).median()
然后用中位数替换原始DataFrame中的负值:
df.mask(df<0, median_df)
你可以这样做:
它对每个冷进行分组,然后找到组的中位数(不包括-1值)
for col in df.columns:
df[col] = df.groupby('Id1')[col].apply(lambda x: (
x.replace(-1, x.loc[x != -1].median())
))
让我们从创建源DataFrame的方式的一个小更正开始:由于每一列可以包含NaN,这是float的特殊情况,创建数据类型为float:
的临时DataFramenp.arange(12, dtype='float')
(创建DataFrame的其余代码不需要更改)。
您将需要以下组处理功能:
def grpProc(grp):
grp[grp == -1] = grp[grp != -1].median()
return grp
从elements !=0计算中位数并保存在elements ==中-1,假设源组(grp)是当前列的一部分对于每个Id1。然后返回修改后的组。
要得到结果,将它应用到DataFrame的每一列,按分组Id1(0级):
result = df.apply(lambda col: col.groupby(level=0).apply(grpProc))
没有传递axis参数,因此此函数应用于每一个(axis == 0).
对于示例数据,结果为:
A B
Id1 Id2
I 0 4.0 NaN
1 2.0 NaN
2 4.0 5.0
J 3 6.0 7.0
I 4 8.0 18.0
J 5 10.0 7.0