计算表中每 x 行的平均值并创建新表
Posted
技术标签:
【中文标题】计算表中每 x 行的平均值并创建新表【英文标题】:Calculate average of every x rows in a table and create new table 【发布时间】:2016-08-17 01:47:22 【问题描述】:我有一个长数据表(约 200 行乘 50 列),我需要创建一个代码来计算每两行和表中每一列的平均值,最终输出是一个新表的平均值。这在 Excel 中显然很疯狂!我使用 python3,我知道一些类似的问题:here、here 和here。但是这些都没有帮助,因为我需要一些优雅的代码来处理多个列并生成一个有组织的数据表。顺便说一句,我的原始数据表已使用 pandas 导入并被定义为数据框,但在 pandas 中找不到简单的方法来执行此操作。非常感谢您的帮助。
表格的一个例子(短版)是:
a b c d
2 50 25 26
4 11 38 44
6 33 16 25
8 37 27 25
10 28 48 32
12 47 35 45
14 8 16 7
16 12 16 30
18 22 39 29
20 9 15 47
预期均值表:
a b c d
3 30.5 31.5 35
7 35 21.5 25
11 37.5 41.5 38.5
15 10 16 18.5
19 15.5 27 38
【问题讨论】:
【参考方案1】:您可以使用df.index//2
创建一个人工组(或者正如@DSM 指出的那样,使用np.arange(len(df))//2
- 以便它适用于所有索引),然后使用 groupby:
df.groupby(np.arange(len(df))//2).mean()
Out[13]:
a b c d
0 3.0 30.5 31.5 35.0
1 7.0 35.0 21.5 25.0
2 11.0 37.5 41.5 38.5
3 15.0 10.0 16.0 18.5
4 19.0 15.5 27.0 38.0
【讨论】:
我在 [这里] 找到了一些接近的东西(***.com/questions/36810595/calculate-average-of-every-x-rows-in-a-table-and-create-new-table)但是,如果您的答案非常优雅和紧凑。太感谢了!只是出于兴趣,df.index//2 中的第一个正斜杠是什么意思? 不客气。这是整数除法,因此 2//2 和 3//2 都产生 1、4//2 和 5//2 产生 2...(并放入同一组)。 知道了!非常感谢! 最好在np.arange(len(df))//2
上分组,以防索引不是简单的 0,1,2.. 等。
快一点的是df.groupby(np.arange(len(df.index))//2).mean()
;)【参考方案2】:
您可以使用pd.rolling()
来解决这个问题,创建一个滚动平均值,然后使用iloc
抓取每个第二个元素
df = df.rolling(2).mean()
df = df.iloc[::2, :]
请注意,第一个观察结果将丢失(即滚动从顶部开始),因此请务必检查您的数据是否按您需要的方式排序。
【讨论】:
这比之前的答案优雅多了。【参考方案3】:NumPythonic 的方法是将元素提取为带有 df.values
的 NumPy 数组,然后沿 axis=1
和 4
沿 axis=2
重塑为带有 2
元素的 3D
数组,并执行平均缩减沿着axis=1
,最后转换回数据框,就像这样-
pd.DataFrame(df.values.reshape(-1,2,df.shape[1]).mean(1))
事实证明,您可以引入 NumPy 的非常高效的工具:np.einsum
来执行此操作 average-reduction
作为 sum-reduction
和 scaling-down
的组合,就像这样 -
pd.DataFrame(np.einsum('ijk->ik',df.values.reshape(-1,2,df.shape[1]))/2.0)
请注意,建议的方法假设行数可以被2
整除。
也如noted by @DSM
,要保留列名,需要在转回Dataframe时加上columns=df.columns
,即-
pd.DataFrame(...,columns=df.columns)
示例运行 -
>>> df
0 1 2 3
0 2 50 25 26
1 4 11 38 44
2 6 33 16 25
3 8 37 27 25
4 10 28 48 32
5 12 47 35 45
6 14 8 16 7
7 16 12 16 30
8 18 22 39 29
9 20 9 15 47
>>> pd.DataFrame(df.values.reshape(-1,2,df.shape[1]).mean(1))
0 1 2 3
0 3 30.5 31.5 35.0
1 7 35.0 21.5 25.0
2 11 37.5 41.5 38.5
3 15 10.0 16.0 18.5
4 19 15.5 27.0 38.0
>>> pd.DataFrame(np.einsum('ijk->ik',df.values.reshape(-1,2,df.shape[1]))/2.0)
0 1 2 3
0 3 30.5 31.5 35.0
1 7 35.0 21.5 25.0
2 11 37.5 41.5 38.5
3 15 10.0 16.0 18.5
4 19 15.5 27.0 38.0
运行时测试 -
在本节中,让我们测试迄今为止列出的所有三种方法来解决性能问题,包括@ayhan's solution with groupby
。
In [24]: A = np.random.randint(0,9,(200,50))
In [25]: df = pd.DataFrame(A)
In [26]: %timeit df.groupby(df.index//2).mean() # @ayhan's solution
1000 loops, best of 3: 1.61 ms per loop
In [27]: %timeit pd.DataFrame(df.values.reshape(-1,2,df.shape[1]).mean(1))
1000 loops, best of 3: 317 µs per loop
In [28]: %timeit pd.DataFrame(np.einsum('ijk->ik',df.values.reshape(-1,2,df.shape[1]))/2.0)
1000 loops, best of 3: 266 µs per loop
【讨论】:
这个比 groupby 快约 2 倍。不错。 @ayhan 是的,我注意到了,即将发布运行时,但最后的想法是让它过去 :) 不过你的是我学到的新东西,熊猫不是我的东西! 请注意,您的 numpy 方法会丢失列名。您可以添加columns=df.columns
来更正此问题。
@DSM 是的,完全错过了!谢谢!将其添加为注释。【参考方案4】:
df.set_index(np.arange(len(df)) // 2).mean(level=0)
【讨论】:
【参考方案5】:在您的情况下,假设您的数据框名称为 new
new = new.groupby(np.arange(len(new)) // 2).mean()
如果想要对列进行平均
new = new.groupby(np.arrange(len(new.columns)) // 2, axis=1).mean()
【讨论】:
【参考方案6】:当我尝试使用numpy
创建人工组时,我得到了ValueError: Grouper and axis must be same length
。作为替代方案,您可以使用 itertools
生成与您的 Dataframe 长度相等的迭代器:
SAMPLE_SIZE = 2
label_series = pd.Series(itertools.chain.from_iterable(itertools.repeat(x, SAMPLE_SIZE) for x in df.index))
sampled_df = df.groupby(label_series).mean()
【讨论】:
以上是关于计算表中每 x 行的平均值并创建新表的主要内容,如果未能解决你的问题,请参考以下文章