【问题标题】:Python: get the element-wise mean of multiple arrays in a dataframePython:获取数据框中多个数组的元素平均值
【发布时间】:2018-02-25 03:14:23
【问题描述】:

我有一个 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 循环而优雅地到达那里?

【问题讨论】:

  • 鉴于混合 dtype 数据(使用 NaN),我认为迭代地执行每个单元格的平均值可能是这里的方法。
  • 我希望避免 for 循环:p
  • 然后查看this post中的两个矢量化解决方案。

标签: python pandas numpy mean elementwise-operations


【解决方案1】:

设置

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]

【讨论】:

  • 非常感谢!非常聪明的想法!如果可以,我有两个后续问题?首先,我不太明白groupby(level=1) 在做什么——如果我在这一步之后打印g,它只显示<pandas.core.groupby.SeriesGroupBy object at 0x000000F86E0A4940>,你能详细说明一下吗?其次,g.mean(axis = 0) 应该和g.apply(np.sum, axis=0) / g.size() 一样,对吧?
【解决方案2】:

方法 #1:循环

鉴于混合 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)

【讨论】:

  • 非常感谢 Divakar 的努力!这个问题似乎有很多解决方案。我想我会使用 piRSquared 的答案,它有点短而且很容易理解。
猜你喜欢
  • 2023-04-07
  • 2020-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-06
  • 1970-01-01
  • 2013-12-29
  • 2019-04-16
相关资源
最近更新 更多