Python 使用groupby()和reduce(),数据分析中经常需要分组数据并总结分组情况。可以用defaultdict(list)
进行分组,然后展开分析。
需要处理的数据实例如下所示:
原始数据序列中每个元素包含一个键值以及对键值的度量。
创建分组的一个常用方法是将键值相同的元素放入同一个列表,如下所示:
上述代码按照键值将可迭代对象中的数据分到不同的组中,其中将可迭代对象中源数据的数据类型定义为D_
,代表每个数据项。用key()
函数从数据项中抽取键值,将返回结果的类型定义为K_
,以与数据项的类型D_
做区分。从示例数据可以看出,每个数据项是一个元组(类型标识符为tuple
),每个键值是一个字符串(类型标识符为str
),键值抽取参数作为可调用函数,将元组类型转换为字符串类型。
key()
函数从数据项中抽取的字符串作为了pd
字典的键值,将数据项追加到了对应的列表中。defaultdict
对象将类型为K_
的键值映射到了类型为List[D_]
的数据列表上。
上述函数返回结果的数据结构与itertools.groupby()
函数的一致,都是由(group key, iterator)
元组组成的可迭代序列,其中分组键值的类型由键值函数的返回值的类型决定,迭代器中则包含一系列原始数据项。
基于itertools.groupby()
实现的分组函数如下所示:
注意:上述两种实现的输入部分有个重要的差别:
groupby()
函数要求数据必须是按键值排序好的,defaultdict
则不需要事先排序。对于大型数据集,不论从时间上还是存储空间上排序,成本都会非常高。
接下来使用上面定义的函数对数据进行分组,可以把该操作作为分组过滤的预处理或分组统计的预处理。
对分组数据的统计汇总如下所示:
partition()
函数的返回结果是一系列(key, iterator)
二元组,summarize()
函数接收这类二元组,从输入数据项中抽取键值和数据迭代器。上面的实现将数据项的类型定义为Item
,包含一个类型为K_
的键值和一个可以转换为浮点数的数值。为了从item_iter
迭代器中抽取出数值部分,使用生成器表达式得到一个仅包含值的元组。
还可以使用(snd, item_iter)
实现从二元组中抽取第二项,并且snd = lambda x: x[1]
。snd
这个名字是从元组中抽取第二项中“第二”(second)的简写。
将summarize()
函数应用于每个分组,如下所示:
或者使用另一个实现方案,如下所示:
两种方法都能计算出每个分组的统计汇总值,计算结果如下所示:
这里算出的方差可用于卡方检验,用于确定数据集的零假设是否成立。零假设指数据不包含有价值的信息:方差是完全随机的。还可以对数据做组间比较,确定各组平均值的变化是否符合零假设,或者存在某种统计意义上的显著变化。