Hadley Wickham(许多热门R语言包的作者)创造了一个用于表示分组运算的术语"split-apply-combine"(拆分-应用-合并),这个词很好的描述了整个过程。分组运算的第一个阶段,pandas对象(无论是Series、DataFrame还是其他的)中的数据会根据你所提供的一个或多个键被拆分(split)为多组。拆分操作是在对象的特定轴上执行的。例如,DataFrame可以在其行(axis=0)或列(axis=1)上进行分组。然后,将一个函数应用(apply)到各个分组并产生一个新值。最后,所有这些函数的执行结果会被合并(combine)到最终的结果对象中去。
PART I. groupby方法PART II. GroupBy对象i) GroupBy对象ii) 对/GroupBy对象进行迭代和转化iii)GroupBy对象的常用方法PART II. groupby方法的分组键i) 列名作为分组键df.groupby('key1').mean()df.groupby(['key1','key2']).mean()ii) 列表作为分组键df.groupby(l)['size'].count()iii) 数组作为分组键df.groupby([states,year])['data1'].mean()iv) 字典作为分组键df.groupby(mapping, axis=1).sum()v) Series作为分组键people.groupby(map_series, axis=1).count()df.groupby([df['key1'],df['key2']]).mean()vi) 索引作为分组键df.groupby(level='cty',axis=1).count()df.groupby(level=0,axis=1).count()vii) dtype作为分组键df.groupby(df.dtypes,axis=1).size()viii)函数作为分组键people.groupby(len).sum()df.groupby(lambda x: x.day)['volume'].sum()ix) 函数跟数组、列表、字典、Series混合使用作为分组键people.groupby([len,key_list]).sum()
PART I . groupby方法
groupby(by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, observed=False, **kwargs)by : mapping, function, label, or list of labelsUsed to determine the groups for the groupby.If ``by`` is a function, it's called on each value of the object'sindex. If a dict or Series is passed, the Series or dict VALUESwill be used to determine the groups (the Series' values are firstaligned; see ``.align()`` method). If an ndarray is passed, thevalues are used as-is determine the groups. A label or list oflabels may be passed to group by the columns in ``self``. Noticethat a tuple is interpreted a (single) key.axis : {0 or 'index', 1 or 'columns'}, default 0Split along rows (0) or columns (1).level : int, level name, or sequence of such, default NoneIf the axis is a MultiIndex (hierarchical), group by a particularlevel or levels.as_index : bool, default TrueFor aggregated output, return object with group labels as theindex. Only relevant for DataFrame input. as_index=False iseffectively "SQL-style" grouped output.sort : bool, default TrueSort group keys. Get better performance by turning this off.Note this does not influence the order of observations within eachgroup. Groupby preserves the order of rows within each group.group_keys : bool, default TrueWhen calling apply, add group keys to index to identify pieces.squeeze : bool, default FalseReduce the dimensionality of the return type if possible,otherwise return a consistent type.observed : bool, default FalseThis only applies if any of the groupers are Categoricals.If True: only show observed values for categorical groupers.If False: show all values for categorical groupers.重点参数:by axis level as_index
PART II. GroupBy对象
i)GroupBy对象
假设想要按key1进行分组,并计算data1列的平均值。实现该功能的方式有很多,而这里要用的是:访问data1,并根据key1调用groupby:
变量grouped是一个SeriesGroupBy对象, 变量grouped2是一个DataFrameGroupBy对象。它们实际上还没有进行任何计算,只是含有一些有关分组键'key1'的中间数据。换言之,该对象已经有了接下来对各分组执行运算所需的信息。
ii)对/GroupBy对象进行迭代和转化
GroupBy对象支持迭代,可以产生一组二元元组(由分组名和数据块组成)
分组键为单列
分组键为多列
通过列表生成式或list()方法可以把groupby对象,转换为分组名+数据块组成的元组的列表,其实我们还可以把groupby对象转换为字典
iii)GroupBy对象的常用方法
GroupBy对象的常用方法除了常见的sum、mean、count等优化好的聚合函数,还有返回不同分组大小的Series的简便方法size(),除此之外还可以对groupby对象传入自定义的函数(agg/apply)。这部分的内容将放在下面一篇详细展开。
例如,我们可以调用GroupBy的mean和count方法来计算分组平均值和分组计数:
groupby对象的size方法
groupby对象的size方法可以返回一个含有分组大小的Series
对groupby对象调用count不行吗?倒也不是不行,就是格式不太友好。
所以还是size方法好
PART III. groupby方法的分组键
分组键的形式:
- 列表或数组,其长度与待分组的轴一样
- 表示DataFrame某个列名的值
- 字典或Series,给出待分组轴上的值与分组名之间的对应关系
- 函数,用于处理轴索引或索引中的各个标签
即 列表/数组、列名、字典、Series、函数
注意,后面三种都只是快捷方式,最终目的都是产生一组用于拆分对象的值,而且任何东西最终都会被转换为数组。
i)列名作为分组键
可以用【一个列名字符串或一个列名字符串的列表】选取部分列进行聚合
对于由DataFrame产生的GroupBy对象,如果用一个列名字符串或一个列名字符串的列表对其进行索引,就能实现选取部分列进行聚合的目的。尤其对于大数据集,很可能只需要对部分列进行聚合。
df.groupby('key1')['data1'] 等价于 df['data1'].groupby(df['key1'])
df.groupby('key1')[['data2']] 等价于 df[['data2']].groupby(df['key1')]
在执行df.groupby('key1').mean()时,结果中没有key2列。这是因为df['key2']不是数值数据(俗称“麻烦列”),所以被从结果中排除了。默认情况下,所有数值列都会被聚合,虽然有时候可能会被过滤为一个子集。
聚合单列并以DataFrame形式得到结果
聚合单列并以Series形式得到结果
ii)列表作为分组键
iii)数组作为分组键
iv)字典作为分组键
列的分组关系如下:
只需把这个包含分组关系的字典传给groupby即可:
v)Series作为分组键
Series也有字典同样的功能,它可以被看作一个固定大小的映射。对于上面那个例子,如果用Series作为分组键,则pandas会检查Series以确保其索引跟分组轴是对齐的。
多个Series作为分组键
.mean()的调用过程先略去不讲。这里最重要的是,数据(Series)根据分组键进行了聚合,产生了一个新的Series,其索引为唯一值。之所以结果中索引的名称为key1、key2,是因为原始DataFrame的列df['key1']、df['key2']就叫这个名字。
通过对两个键进行分组,得到的Series具有一个层次化索引(由唯一的键值对组成),reshape一下:
vi)索引作为分组键
向level关键字传入级别编号或名称,根据索引级别分组
层次化索引最方便的地方在于它能够根据索引级别进行聚合。要实现该目的,通过level关键字传入级别编号或名称即可:
vii)dtype作为分组键
在axis=1上根据dtype对列进行分组
viii)函数作为分组键
相较于字典或Series,Python函数在定义分组映射关系时可以更有创意且更为抽象。任何被当作分组键的函数都会在各个索引值上被调用一次,其返回值就会被用作分组名称。( If ``by`` is a function, it's called on each value of the object's index.)
假设我们现在想根据人名的长度进行分组:
函数作为分组键很灵活,下面是12月1日1分钟K线的数据,求这一天的总成交量,除了用df['volume'].sum(),还可以用将lambda函数作为分组键进行聚合的方法实现
>>>path = r"C:UsersbyqpzDesktop现货策略现货高频新策略12.01.csv"
>>>df = pd.read_csv(path, engine='python',encoding='utf_8_sig',index_col='time',parse_dates=True)
>>>df['volume'].sum()
119939049.16099769
>>>df.groupby(lambda x: x.day)['volume'].sum()
1 1.199390e+08
Name: volume, dtype: float64
ix)函数跟数组、列表、字典、Series混合使用作为分组键
混合使用不是问题,因为任何东西最终都会被转换为数组: