返回介绍

数学基础

统计学习

深度学习

工具

Scala

八、 聚合与分组

发布于 2023-07-17 23:38:23 字数 15766 浏览 0 评论 0 收藏 0

1. 分组

  1. 分组运算的过程为:拆分-应用-合并

    • 拆分阶段:Series/DataFrame等数据根据你提供的一个或者多个键,被拆分为多组
    • 应用阶段:根据你提供的一个函数应用到这些分组上
    • 合并阶段:将函数的执行结果合并到最终结果中
  2. 分组中有两种数据:源数据(被分组的对象),分组数据(用于划分源数据的)。

    • 源数据每一行(axis=0) 对应于分组数据中的一个元素。分组数据中每一个唯一值对应于一个分组。
    • 当分组数据也在源数据中时,可以直接通过指定列名来指定分组数据(值相同的为同一组)。
  3. .groupby()方法是分组方法:

    
    
    xxxxxxxxxx
    Series/DataFrame.groupby(by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, **kwargs)
    • by:一个mapping functionlist of function、一个字典、一个序列、一个元组、一个list of column name。它指定了分组数据。

      • 如果传入了函数,则在每个index value上调用函数来产生分组数据

      • 如果是Series或者字典,则根据每个index value在字典/Series中的值来产生分组数据

      • 如果是个column label,则使用该label抽取出来的一列数据产生分组数据

      • 如果是个column labellist,则使用一组column label抽取出来的多列数据作为分组数据。

      • 如果是个序列,则它直接指定了分组数据。

      • 如果是个序列的序列,则使用这些序列拼接成一个MulitiIndex,然后根据这个MultiIndex替换掉index后,根据label value来分组。(事实上并没有替换,只是用于说明这个过程)

        如果axis=1,则index label替换成column label

    • axis:指定沿着哪个轴分组。可以为0/'index',表示沿着 0轴。可以为1/'columns',表示沿着 1轴

    • level:一个整数、level name或者其序列。如果axis是个MultiIndex,则在指定级别上的索引来分组

    • as_index:一个布尔值。如果为True,则将group label作为输出的index。如果为False,则输出是SQL风格的分组(此时分组的key作为一列,而不是作为index)。Series中,该参数必须为True

    • sort:一个布尔值。如果为True,则对分组的键进行排序。

    • group_keys:一个布尔值。如果为True,且调用了函数来决定分组,则添加分组键来区分不同的数据(否则你不知道每一行数据都对应于哪里)

    • squeeze:一个布尔值。如果为True,则尽可能的缩减结果的类型。

    该函数返回一个GroupBy对象。

    groupby0 groupby1 groupby2

  4. 我们可以使用dtype来分组,此时by=df.dtypes,axis=1groupby_dtype

  5. 对于由DataFrame产生的GroupBy对象,我们可以用一个或者一组列名对其索引。它其实一个语法糖。

    • 如果索引是一个列名,则df.groupby('key1')['data1'] 等价于df['data1'].groupby(df['key1'])

    • 如果索引是一个元组和序列,则 df.groupby('key1')[['data1','data2']] 并不等价于 df[['data1','data2']].groupby(df['key1']),而是等同于 df.groupby(df['key1'])

      • 之所以用 [['data1','data2']],是因为df[['data1','data2']]df['data1','data2']语义不同。后者表示某个label是个元组,该元组的值为'data1','data2'

groupby_sugar

2. GroupBy对象

  1. GroupBy对象是一个迭代器对象。迭代结果产生一组二元元组(由分组名和数据块组成)。

    • 如果有多重键,则元组的第一个元素将是由键组成的元组。
    • dict(list(GroupBy_obj))将生产一个字典,方便引用

    GroupBy_iter1 GroupBy_iter2

    • GroupBy.groups属性返回一个字典: {group name->group labels}
    • GroupBy.indices属性返回一个字典:{group name->group indices}

    GroupBy_groups_indices

  2. GroupBy的统计函数有(排除了NaN):

    • GroupBy.count() :计算各分组的非NaN的数量
    • GroupBy.cumcount([ascending]):计算累积分组数量
    • GroupBy.first():计算每个分组的第一个非NaN
    • GroupBy.head([n]) :返回每个分组的前 n个值
    • GroupBy.last() :计算每个分组的最后一个非NaN
    • GroupBy.max():计算每个分组的最大值
    • GroupBy.mean(*args, **kwargs):计算每个分组的均值
    • GroupBy.median():计算每个分组的中位数
    • GroupBy.min():计算每个分组的最小值
    • GroupBy.nth(n[, dropna]):计算每个分组第n行数据。 如果n是个整数列表,则也返回一个列表。
    • GroupBy.ohlc():计算每个分组的开始、最高、最低、结束值
    • GroupBy.prod():计算每个分组的乘
    • GroupBy.size():计算每个分组的大小(包含了NaN
    • GroupBy.sem([ddof]) :计算每个分组的sem(与均值的绝对误差之和)
    • GroupBy.std([ddof]) :计算每个分组的标准差
    • GroupBy.sum():计算每个分组的和
    • GroupBy.var([ddof]):计算每个分组的方差
    • GroupBy.tail([n]) :返回每个分组的尾部 n个值

    另外SeriesGroupBy/DataFrameGroupBy也支持Series/DataFrame的统计类方法以及其他方法:

    
    
    xxxxxxxxxx
    #SeriesGroupBy - DataFrameGroupBy 都有的方法: .agg(arg, *args, **kwargs) .all([axis, bool_only, ...]) .any([axis, bool_only, ...]) .bfill([limit]) .corr([method, min_periods]) .count() .cov([min_periods]) .cummax([axis, skipna]) .cummin([axis, skipna]) .cumprod([axis]) .cumsum([axis]) .describe([percentiles, ...]) .diff([periods, axis]) .ffill([limit]) .fillna([value, method, ...]) .hist(data[, column, by, ...]) .idxmax([axis, skipna]) .idxmin([axis, skipna]) .mad([axis, skipna, level]) .pct_change([periods, ...]) .plot .quantile([q, axis, ...]) .rank([axis, method, ...]) .resample(rule, *args, **kwargs) .shift([periods, freq, axis]) .size() .skew([axis, skipna, level, ...]) .take(indices[, axis, ...]) .tshift([periods, freq, axis]) #SeriesGroupBy独有的方法 SeriesGroupBy.nlargest(*args, **kwargs) SeriesGroupBy.nsmallest(*args, **kwargs) SeriesGroupBy.nunique([dropna]) SeriesGroupBy.unique() SeriesGroupBy.value_counts([normalize, ...]) #DataFrameGroupBy独有的方法 DataFrameGroupBy.corrwith(other[, axis, drop]) DataFrameGroupBy.boxplot(grouped[, ...])

    GroupBy_method0 GroupBy_method1

  3. 如果你希望使用自己的聚合函数,只需要将其传入.aggregate(func, *args, **kwargs)或者.agg()方法即可。其中func接受一维数组,返回一个标量值。

    • 注意:自定义聚合函数会慢得多。这是因为在构造中间分组数据块时存在非常大的开销(函数调用、数据重排等)
    • 你可以将前面介绍的GroupBy的统计函数名以字符串的形式传入。
    • 如果你传入了一组函数或者函数名,则得到的结果中,相应的列就用对应的函数名命名。如果你希望提供一个自己的名字,则使用(name,function)元组的序列。其中name用作结果列的列名。
    • 如果你希望对不同的列采用不同的聚合函数,则向agg()传入一个字典。字典的键就是列名,值就是你希望对该列采用的函数。

    GroupBy_agg0 GroupBy_agg1

  4. .get_group(key)可以获取分组键对应的数据。

    • key :不同的分组就是依靠它来区分的
  5. GroupBy的下标操作将获得一个只包含源数据中指定列的新GroupBy对象

  6. GroupBy类定义了__getattr__()方法,当获取GroupBy中未定义的属性时:

    • 如果属性名是源数据对象的某列的名称则,相当于GroupBy[name],即获取针对该列的GroupBy对象
    • 如果属性名是源数据对象的方法,则相当于通过.apply(name)对每个分组调用该方法。

3. 分组级运算

  1. agg/aggregate只是分组级运算其中的一种。它接受一维数组,返回一个标量值。

  2. transform是另一个分组级运算。它也接受一维数组。只能返回两种结果:要么是一个标量值(该标量值将被广播),或者一个相同大小的结果数组。

    • 你无法通过字典来对不同的列进行不同的transform
    
    
    xxxxxxxxxx
    GroupBy.transform(func, *args, **kwargs)

    GroupBy_transform

  3. apply是另一个分组级运算。它是最一般化的分组级运算。它将待处理的对象拆分成多个片段,然后对各个片段调用传入的函数,最后尝试将各个片段组合到一起。

    
    
    xxxxxxxxxx
    GroupBy.apply(func, *args, **kwargs)
    • func:运算函数。其第一个位置参数为待处理对象。其返回值是一个标量值或者pandas对象。
    • args/kwargs是传递给func的额外的位置参数与关键字参数。

    对于DataFrame.groupby时,传递给func的第一个参数是DataFrame;对于Series.groupby,传递给func的第一个参数是Series

    GroupBy_apply

  4. pd.cut()/qcut()函数返回的是Categorical对象。我们可以用它作为.groupby()by参数的值。这样可以实现桶分析。 GroupBy_cut

4. 透视表和交叉表

  1. 透视表pivot table是一种数据汇总工具。它根据一个或者多个键对数据进行聚合,并根据行和列上的分组键将数据分配到各个单元格中。

    • 你可以通过.groupby功能以及索引的变换来手工实现这种功能
  2. DataFrame.pivot_table()方法,以及pandas.pivot_table()函数都可以实现这种功能

    
    
    xxxxxxxxxx
    pandas.pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All')
    • data:一个DataFrame对象
    • values:指定哪些列将被聚合。默认聚合所有的数值列。
    • index:一个index label、一个Grouper、一个数组,或者前面这些类型的一个列表。它指定关于分组的列名或者其他分组键,出现在结果透视表的行
    • columns:一个column label、一个Grouper、一个数组,或者前面这些类型的一个列表。它指定关于分组的列名或者其他分组键,出现在结果透视表的列
    • aggfunc:一个函数或者函数的列表。默认为numpy.mean。它作为聚合函数。如果为函数的列表,则结果中会出现多级索引,函数名就是最外层的索引名。
    • fill_value:一个标量,用于替换NaN
    • margins:一个布尔值。如果为True,则添加行/列的总计。
    • dropna:一个布尔值。如果为True,则结果不包含这样的列:该列所有元素都是NaN
    • margins_name:一个字符串。当margins=True时,margin列的列名。

    pivot_table0 pivot_table1 pivot_table2

  3. 交叉表cross-tabulation:crosstab是一种用于计算分组频率的特殊透视表。我们可以使用pivot_table()函数实现透视表的功能,但是直接使用更方便:

    
    
    xxxxxxxxxx
    pandas.crosstab(index, columns, values=None, rownames=None, colnames=None, aggfunc=None, margins=False, dropna=True, normalize=False)
    • index:一个array-likeSeries或者前两种的列表。它给出了行的计算频数的数据。

    • columns:一个array-likeSeries或者前两种的列表。它给出了列的计算频数的数据。

    • values:一个array-like,该数据用于聚合。如果出现了values,则必须提供aggfunc

    • aggfunc:一个函数对象,是聚合函数。如果出现了aggfunc,则必须提供values

    • rownames:一个序列。如果非空,则必须和结果的row indexlevel数量相等

    • colnames:一个序列。如果非空,则必须和结果的column indexlevel数量相等

    • margins:一个布尔值。如果为True,则添加行/列的总计。

    • dropna:一个布尔值。如果为True,则结果不包含这样的列:该列所有元素都是NaN

    • normalize:一个布尔值、字符串('all'/'index'/'columns')、或者整数0/1。它指定是否进行归一化处理(归一化为频率),否则就是频数。

      • 如果'all'/True,则对所有数据进行归一化
      • 如果为'index':则对每一行归一化
      • 如果为'columns':则对每一列归一化
      • 如果marginsTrue,则对margins也归一化。

    values的作用是这样的:首先根据index-columns建立坐标。行坐标来自index,列坐标来自columns。在index-columns-values中,同一个坐标下的values组成Series。这个Seriesaggfunc进行聚合,aggfunc接受一个Series,返回一个标量。此时就不再是对坐标点进行计数了,而是对values进行聚合。

    crosstab0 crosstab1 crosstab2

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文