Python:获取数据帧中多个数组的逐元素平均值
Python: get the element-wise mean of multiple arrays in a dataframe
我有一个 16x10 的熊猫数据框,每个单元格中有 1x35000 个数组(或 NaN)。我想对每一列的行进行逐元素平均。
1 2 3 ... 10
1 1x35000 1x35000 1x35000 1x35000
2 1x35000 NaN 1x35000 1x35000
3 1x35000 NaN 1x35000 NaN
...
16 1x35000 1x35000 NaN 1x35000
为避免误解:取第一列中每个数组的第一个元素并取平均值。然后取第一列中每个数组的第二个元素,再次取平均值。最后我想要一个 1x10 数据帧,每列有一个 1x35000 数组。该数组应该是我以前的数组的逐元素平均值。
1 2 3 ... 10
1 1x35000 1x35000 1x35000 1x35000
你有没有最好不用 for 循环优雅地到达那里的想法?
设置
np.random.seed([3,14159])
df = pd.DataFrame(
np.random.randint(10, size=(3, 3, 5)).tolist(),
list('XYZ'), list('ABC')
).applymap(np.array)
df.loc['X', 'B'] = np.nan
df.loc['Z', 'A'] = np.nan
df
A B C
X [4, 8, 1, 1, 9] NaN [8, 2, 8, 4, 9]
Y [4, 3, 4, 1, 5] [1, 2, 6, 2, 7] [7, 1, 1, 7, 8]
Z NaN [9, 3, 8, 7, 7] [2, 6, 3, 1, 9]
解决方案
g = df.stack().groupby(level=1)
g.apply(np.sum, axis=0) / g.size()
A [4.0, 5.5, 2.5, 1.0, 7.0]
B [5.0, 2.5, 7.0, 4.5, 7.0]
C [5.66666666667, 3.0, 4.0, 4.0, 8.66666666667]
dtype: object
如果你坚持你呈现的形状
g = df.stack().groupby(level=1)
(g.apply(np.sum, axis=0) / g.size()).to_frame().T
A B C
0 [4.0, 5.5, 2.5, 1.0, 7.0] [5.0, 2.5, 7.0, 4.5, 7.0] [5.66666666667, 3.0, 4.0, 4.0, 8.66666666667]
方法 #1:Loopy
鉴于混合 dtype 输入数据,我们可能希望循环以提高性能效率。因此,可以建议使用显式循环或引擎盖下使用.apply/.applymap
的解决方案。
这是循环遍历列的一种方法 -
mask = ~df.isnull().values
n = df.shape[1]
out = np.empty((1,n),dtype=object)
for i in range(n):
out[0,i] = df.iloc[mask[:,i],i].mean()
df_out = pd.DataFrame(out)
样本输入、输出-
In [326]: df
Out[326]:
0 1 2
0 [4, 0, 1, 6] [4, 2, 2, 2] [5, 3, 5, 4]
1 NaN [0, 5, 6, 8] NaN
2 NaN NaN NaN
3 NaN NaN NaN
In [327]: df_out
Out[327]:
0 1 2
0 [4.0, 0.0, 1.0, 6.0] [2.0, 3.5, 4.0, 5.0] [5.0, 3.0, 5.0, 4.0]
方法 #2:向量化
如果您必须矢量化,这里有一种使用 matrix-multiplication
替换 mean-reductions
的方法,这可能会改进大数据 -
mask = ~df.isnull().values
v = np.vstack(df.values[mask])
r,c = np.where(mask)
n = df.shape[1]
pos_mask = c == np.arange(n)[:,None]
out = pos_mask.dot(v)/np.bincount(c).astype(float)[:,None]
df_out1 = pd.DataFrame(out)
示例输出 -
In [328]: df_out1
Out[328]:
0 1 2 3
0 4.0 0.0 1.0 6.0
1 2.0 3.5 4.0 5.0
2 5.0 3.0 5.0 4.0
方法 #3:再向量化一个
利用np.add.reduceat
得到那些mean-reductions
-
mask = ~df.T.isnull().values
v = np.vstack(df.values.T[mask])
count = mask.sum(1)
out0 = np.add.reduceat(v, np.r_[0,count.cumsum()[:-1]])
out = out0/count[:,None].astype(float)
df_out2 = pd.DataFrame(out)
我有一个 16x10 的熊猫数据框,每个单元格中有 1x35000 个数组(或 NaN)。我想对每一列的行进行逐元素平均。
1 2 3 ... 10
1 1x35000 1x35000 1x35000 1x35000
2 1x35000 NaN 1x35000 1x35000
3 1x35000 NaN 1x35000 NaN
...
16 1x35000 1x35000 NaN 1x35000
为避免误解:取第一列中每个数组的第一个元素并取平均值。然后取第一列中每个数组的第二个元素,再次取平均值。最后我想要一个 1x10 数据帧,每列有一个 1x35000 数组。该数组应该是我以前的数组的逐元素平均值。
1 2 3 ... 10
1 1x35000 1x35000 1x35000 1x35000
你有没有最好不用 for 循环优雅地到达那里的想法?
设置
np.random.seed([3,14159])
df = pd.DataFrame(
np.random.randint(10, size=(3, 3, 5)).tolist(),
list('XYZ'), list('ABC')
).applymap(np.array)
df.loc['X', 'B'] = np.nan
df.loc['Z', 'A'] = np.nan
df
A B C
X [4, 8, 1, 1, 9] NaN [8, 2, 8, 4, 9]
Y [4, 3, 4, 1, 5] [1, 2, 6, 2, 7] [7, 1, 1, 7, 8]
Z NaN [9, 3, 8, 7, 7] [2, 6, 3, 1, 9]
解决方案
g = df.stack().groupby(level=1)
g.apply(np.sum, axis=0) / g.size()
A [4.0, 5.5, 2.5, 1.0, 7.0]
B [5.0, 2.5, 7.0, 4.5, 7.0]
C [5.66666666667, 3.0, 4.0, 4.0, 8.66666666667]
dtype: object
如果你坚持你呈现的形状
g = df.stack().groupby(level=1)
(g.apply(np.sum, axis=0) / g.size()).to_frame().T
A B C
0 [4.0, 5.5, 2.5, 1.0, 7.0] [5.0, 2.5, 7.0, 4.5, 7.0] [5.66666666667, 3.0, 4.0, 4.0, 8.66666666667]
方法 #1:Loopy
鉴于混合 dtype 输入数据,我们可能希望循环以提高性能效率。因此,可以建议使用显式循环或引擎盖下使用.apply/.applymap
的解决方案。
这是循环遍历列的一种方法 -
mask = ~df.isnull().values
n = df.shape[1]
out = np.empty((1,n),dtype=object)
for i in range(n):
out[0,i] = df.iloc[mask[:,i],i].mean()
df_out = pd.DataFrame(out)
样本输入、输出-
In [326]: df
Out[326]:
0 1 2
0 [4, 0, 1, 6] [4, 2, 2, 2] [5, 3, 5, 4]
1 NaN [0, 5, 6, 8] NaN
2 NaN NaN NaN
3 NaN NaN NaN
In [327]: df_out
Out[327]:
0 1 2
0 [4.0, 0.0, 1.0, 6.0] [2.0, 3.5, 4.0, 5.0] [5.0, 3.0, 5.0, 4.0]
方法 #2:向量化
如果您必须矢量化,这里有一种使用 matrix-multiplication
替换 mean-reductions
的方法,这可能会改进大数据 -
mask = ~df.isnull().values
v = np.vstack(df.values[mask])
r,c = np.where(mask)
n = df.shape[1]
pos_mask = c == np.arange(n)[:,None]
out = pos_mask.dot(v)/np.bincount(c).astype(float)[:,None]
df_out1 = pd.DataFrame(out)
示例输出 -
In [328]: df_out1
Out[328]:
0 1 2 3
0 4.0 0.0 1.0 6.0
1 2.0 3.5 4.0 5.0
2 5.0 3.0 5.0 4.0
方法 #3:再向量化一个
利用np.add.reduceat
得到那些mean-reductions
-
mask = ~df.T.isnull().values
v = np.vstack(df.values.T[mask])
count = mask.sum(1)
out0 = np.add.reduceat(v, np.r_[0,count.cumsum()[:-1]])
out = out0/count[:,None].astype(float)
df_out2 = pd.DataFrame(out)