2018年5月20日
对数据集进行分组并对各组应用一个函数(无论是聚合还是转换)是数据分析工作中的重要环节。pandas提供了一个灵活高效的groupby功能,它能帮助你以一种自然的方式对数据集进行切片,切块,摘要等操作。
分组运算的第一个阶段,pandas对象(无论是Series、DataFrame还是其他的)中的数据会根据你所提供的一个或多个键(键可以是任意长度适当的数组,可以是pandas对象原本的索引,可以是pandas对象的行或列,还可以是自行添加的任意长度适当的数组)被拆分(split)为多组。
拆分操作是在对象的特定轴上执行的。例如DataFrame可以在其行(axis=0)或列(axis=1)上进行分组。然后,将一个函数应用(apply)到各个分组并产生一个新值。最后,所有这些函数的执行结果会被合并(combine)到最终的结果对象中。结果对象的形式一般取决于数据上所执行的操作。
多说无益,下面使用groupby函数来实现一些有用的功能。首先是实现层次化索引下特定级别的数据取均值,在进行实验时,为了减小测量的误差,常常使用相同的实验参数进行多次测量,最后使用多次测量的平均值来作为最终实验结果;此外,在实际实验中常常在多个维度上改变变量,反映在DataFrame数据结构中就是采用多重索引来标记数据。groupby函数提供了高效解决此类问题的途径。
如上图所示,上图显示的是具有层次化索引的数据,其中第二重索引的波长项存在重复索引值,这是重复测量的结果。在实际分析数据时,需要对重复索引的行进行取平均运算,来得到唯一的索引值。这样的取平均运算通过groupby函数一行代码即可以实现:
# decay_data_all_df为存储数据的dataframe
# 注:2019年6月14日更新,新的pandas版本(0.24.2)在直接使用zip()函数解压得到的列表后会报keyerror错误
# 原因是列表的元素为元组,解决方法是把元组转化为列表,如下所示
# idxTuples中元素为元组,新版本中(pandas 0.24.2)groupby会报错,需要转化为list
idxTuples = list(zip(*decay_data_all_dataframe.index.values))
newBy = []
for tup in idxTuples:
newBy.append(list(tup))
decay_data_df = decay_data_all_dataframe.groupby(newBy).mean()
# 首先通过decay_data_all_dataframe.index.values得到索引的列表, 列表元素是包括多重索引的各个索引值的元组。
# 如果直接将该列表传递的groupby,分组将以元组为键拆分数据,这不是我们想要的结果
# 为了得到和处理前一样的多重索引结构,使用list(zip(*list))进行解包后转换为列表
# 得到仅包含单一索引层级元素的元组组成的列表(此处为包含两个元组的列表)
# 再对拆分后的数据进行mean()运算,合并后得到唯一索引值
当然这只是groupby操作能实现的多种功能之一,除此之外还有丰富的功能应用:如在上个例子的取平均运算后,我还需要将单一激发功率下不同波长的数据进行求和。groupby函数同样可以一行代码实现功能:
decay_data_sum_df = decay_data_df.groupby(level='Power Set').sum()
# Power Set 为level=0的索引,根据该索引对数据分组后,进行求和sum()运算
# 求和运算后的结果即是不同波长数据求和的结果
上述的示例充分体现了groupby功能的强大,这款强大的工具还有许多其他方面有待探索。