使用特定列中的行平均值更有效地填充NaN

时间:2018-12-12 12:53:03

标签: python pandas

感谢您抽出时间阅读我的问题。

我想用名称以“ A”开头的列中的平均值填充以下df中的NaN。

例如,第一个NaN应填充2.5,这是2和3的平均值。最后一个NaN应替换为1.5。尽管列中以“ B”开头的任何值都位于同一df中。

A.1.a  A.3.d  A.6.i  B.2.b
NaN    2      3      12
1      2      3      12
1      NaN    3      12
1      2      3      12
NaN    2      3      12
1      2      NaN    12

这是我的成功尝试。

# read only columns which names started with A. 
cols_A = [col for col in df if col.startswith('A')]   
cols_A = df[cols_A]

cols_A = cols_A.apply(lambda row: row.fillna(row.mean()), axis=1)
cols_A

我正在寻找一种更有效的方法,因为我的df有更多的列。

3 个答案:

答案 0 :(得分:1)

仅用A替换的Numpy解决方案开始了以下列:

#select only A starting columns
mask = df.columns.str.startswith('A')
df1 = df.loc[:, mask]
print (df1)
   A.1.a  A.3.d  A.6.i
0    NaN    2.0    3.0
1    1.0    2.0    3.0
2    1.0    NaN    3.0
3    1.0    2.0    3.0
4    NaN    2.0    3.0
5    1.0    2.0    NaN

#convert to 2d array
arr = df1.values
#broadcast to 2d array by df1 shape
a = np.broadcast_to(np.nanmean(arr, axis=1)[:, None], df1.shape)
#check missing values
m = np.isnan(arr)
#replace them by mask
arr[m] = a[m]
print (arr)
[[2.5 2.  3. ]
 [1.  2.  3. ]
 [1.  2.  3. ]
 [1.  2.  3. ]
 [2.5 2.  3. ]
 [1.  2.  1.5]]

#assign back
df.loc[:, mask] = arr
print (df)
   A.1.a  A.3.d  A.6.i  B.2.b
0    2.5    2.0    3.0     12
1    1.0    2.0    3.0     12
2    1.0    2.0    3.0     12
3    1.0    2.0    3.0     12
4    2.5    2.0    3.0     12
5    1.0    2.0    1.5     12

如果需要用列的第一个值定义的组替换NaN,则

df = df.combine_first(df.groupby(lambda x: x[0], axis=1).transform('mean'))
#alternative
#df = df.combine_first(df.groupby(df.columns.str[0], axis=1).transform('mean'))
print (df)
   A.1.a  A.3.d  A.6.i  B.2.b
0    2.5    2.0    3.0     12
1    1.0    2.0    3.0     12
2    1.0    2.0    3.0     12
3    1.0    2.0    3.0     12
4    2.5    2.0    3.0     12
5    1.0    2.0    1.5     12

另一个想法是创建Series的字典并替换为DataFrame.fillna

df1 = df.groupby(df.columns.str[0], axis=1).mean()

df = df.fillna({x: df1[x[0]] for x in df.columns})
print (df)
   A.1.a  A.3.d  A.6.i  B.2.b
0    2.5    2.0    3.0     12
1    1.0    2.0    3.0     12
2    1.0    2.0    3.0     12
3    1.0    2.0    3.0     12
4    2.5    2.0    3.0     12
5    1.0    2.0    1.5     12

答案 1 :(得分:1)

IIUC,您可以尝试broadcastingfillna

cols = [x for x in df.columns if x.startswith('A')]
df.fillna(pd.DataFrame((df[cols].sum(1)/df[cols].notnull().sum(1)).values[:,None] * np.ones([len(cols),1]).T, columns=cols))

    A.1.a   A.3.d   A.6.i   B.2.b
0   2.5     2.0     3.0     12
1   1.0     2.0     3.0     12
2   1.0     2.0     3.0     12
3   1.0     2.0     3.0     12
4   2.5     2.0     3.0     12
5   1.0     2.0     1.5     12

好的时机

df = pd.concat([df]*1000).reset_index(drop=True)

%timeit df.fillna(pd.DataFrame(df[cols].sum(1).div(df[cols].notnull().sum(1)).values[:,None] * np.ones([len(cols),1]).T, columns=cols))
5.73 ms ± 272 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit df.combine_first(df.groupby(lambda x: x[0], axis=1).transform('mean'))
856 ms ± 22.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

答案 2 :(得分:0)

另一个选择是:

cols_A = cols_A.T.fillna(cols_A.mean(axis=1)).T

输出:

   A.1.a  A.3.d  A.6.i
0    2.5    2.0    3.0
1    1.0    2.0    3.0
2    1.0    2.0    3.0
3    1.0    2.0    3.0
4    2.5    2.0    3.0
5    1.0    2.0    1.5