关于Pandas版本: 本文基于 pandas2.1.2 编写。

关于本文内容更新: 随着pandas的stable版本更迭,本文持续更新,不断完善补充。

Pandas稳定版更新及变动内容整合专题: Pandas稳定版更新及变动迭持续更新。

Pandas API参考所有内容目录

本节目录

  • Pandas.DataFrame.groupby()
    • 语法:
    • 返回值:
    • 参数说明:
      • by 指定分组依据
      • axis 指定分割方向
      • level 指定多层索引的层级编号或层级名称
      • as_index 排序方法(升序或降序)
      • sort 是否对组名排序
      • group_keys 是否返回组键
      • observed 只显示观测值或显示所有值
      • dropna 其他排序置
    • 相关方法:
    • 示例:

Pandas.DataFrame.groupby()

DataFrame.groupby() 方法用于使用映射器或指定的列,对 DataFrame 进行数据分组,可以实现类似Excel的数据透视、分类汇总的效果。

  • DataFrame.groupby() 的底层逻辑是:

    • 1、根据指定的规则(由 by 参数指定)分割 DataFramegroupby 对象;
      • 此时只是完成了 DataFrame 分割,仅仅是一个 groupby 对象,还没有完成汇总。这意味着无法像观察 DataFrame 一样观察它:
      • 可以使用 for 循环观察groupby 对象。 例1
      • 用于分组的分类内容,默认会作为这个新 DataFrame 的索引(行索引,或列名,具体视汇总方向而定)。例2-5
    • 2、应用指定的方法,汇总、聚合被分割的数据。
      • 如果应用一种汇总计算方法,所有列都是用一种汇总方法进行聚合。例2
      • 也可以通过 DataFrame.agg 指定不同的列使用不同的计算方法作为汇总方式。例2-4
    • 3、应用聚合方法之后,DataFrame.groupby() 会自动的将聚合后的数据合并为新的 DataFrame

    ⚠️ 注意:

    1、数据分割实际上是基于行索引进行的。

    2、你指定的分割依据(分组依据)需要尽可能的,和行索引等长。

数据分组流程示意图

语法:

DataFrame.groupby (by=None, axis=_NoDefault.no_default, level=None, as_index=True, sort=True, group_keys=True, observed=_NoDefault.no_default, dropna=True)

返回值:

  • pandas.api.typing.DataFrameGroupBy
    • 返回包含分组信息的 groupby 对象。

参数说明:

by 指定分组依据

  • **by:**mapping, function, label, pd.Grouper or list of such

    by 参数用于指定分组的依据(即分割DataFrame的依据):

    • label(列名):用于把某列指定为分组依据

      • 当某列的数据具有分类特性,指定这个列的列名,作为分组依据 DataFrame例1
    • mapping(映射):用于直接把行索引的值指定为分组依据

      • dict(字典):适用于行索引的值可以拿来做分组(常用于分组名称的重命名) 例3
        • 传递一个字典,字典的键是行索引里的可以作为分组的值,字典的值你自定义的分组名;
        • 注意!如果只传递字典,你需要提前准备好行索引。并且行索引里的值,应该是可以有效分组的。
      • Series(序列):适用于你有一个和 DataFrame 行索引等长的 Series 例4
        • 这个 Series 里的值,应该是可以有效分组的;
        • 这个 Series 建议和 DataFrame 行索引等长;
        • 如果这个Series 必须和 DataFrame 行索引不等长,会自动进行对齐(.align()),二者数据量如果差距太大,会产生很多缺失值,造成分组后计算不精准的结果。
    • function(函数): 函数将作用于行索引的每个值,并使用处理后的值,作为分组依据。 例5

      • 行索引中被函数处理后的值,并不会影响计算前的 groupby 对象。例5-1
      • 行索引中被函数处理后的值,将展示在完成分组计算,合并后的 DataFrame例5-2
    • pd.Grouper:通常用于按照时间间隔分组,直接作用于行索引 例6

    • list of such:多个列构成多维度分组汇总

      • 列名列表: 常用于多维度分组汇总,列表里的第1个列名,默认作为顶层行索引,和其他列名构成多层索引。 例7

axis 指定分割方向

  • axis: {0 or ‘index’, 1 or ‘columns’}, default 0

    axis 参数用于指定分割方向(可以参照此图,了解什么是分割 数据分组流程示意图):

    • 0 or ‘index’: 默认为按行索引分割。
    • 1 or ‘columns’: 按列分割。

    弃用于 Pandas 2.1.0 :

    • axis=1Pandas 2.1.0 版本标记为弃用。使用以下替代方法实现:

      • 先 转置 再 分组 frame.T.groupby(...) 例8
    • 这样做的目的是:使分组后数据尽可能的保持更多的操作性和可读性。

level 指定多层索引的层级编号或层级名称

  • level: int, level name, or sequence of such, default None 例9

    如果 DataFrame 具有多层索引,可以用level参数指定级别的编号或名称,不能和 by 参数同时使用。

    • int:整数层级编号 可以用 整数层级编号 指定分组依据。例9-2
    • level name:层级名称 可以用层级名称, 指定分组依据。例9-2
    • sequence of such:层级编号列表,或层级名称列表 可以用层级编号列表,或层级名称列表指定多个分组依据,类似于 by 参数传递列名列表。例9-3

as_index 排序方法(升序或降序)

  • as_index: bool, default True 例10

    as_index 参数控制是否将组标签作为索引返回。

    • as_index=True 时,组标签将成为输出 DataFrame 的索引。
    • as_index=False 时,组标签不会成为索引,而是返回一个类似 SQL 风格的输出。

sort 是否对组名排序

  • sort: bool, default True 例11

    sort 参数用于控制是否对分组名进行排序,默认 sort=True 会对组名进行排序。此参数不会影响每个组内观察值的顺序:

    • True:分组名进行排序。
    • False: 关闭分组名排序,如果关闭,则组将按其在原始 DataFrame 中的顺序显示,可以获得更高的性能。

    改动于 Pandas 2.0.0 :

    自 Pandas2.0.0 开始,当使用 有序分类 数据进行分组,当 sort=False 将不再对其进行排序。

    在之前的版本中(2.0.0 之前),即使设置了 sort=False,对于有序分类,仍然会对分类进行排序。而在 2.0.0 版本中,这个行为发生了变化,即设置 sort=False 不再影响有序分类的排序,保留原始顺序。

    这个改动的目的是为了提供更一致的行为,使得在使用 sort=False 时,无论分类是否有序,都不再对分类进行排序,从而减少用户的困惑。

group_keys 是否返回组键

  • group_keys: bool, default True

    分组的键指的是 groupby 对象 各分组的行索引

    当使用 groupby 调用 applyby 参数生成分组结果时, 并且 结果行索引数量groupby 对象分组数量 不匹配(不匹配则意味着无法汇总),则默认会将 groupby 对象各分组的行索引结果行索引 组合为多层行索引,以便观察。 例12

    • group_keys=True 时(默认值),分组的键会作为结果的索引。这意味着返回的对象会是一个带有分组键的多层次索引的 DataFrame(或者 Series,具体取决于你应用 groupby 的对象是 DataFrame 还是 Series)。
    • group_keys=False 时,分组的键不会作为索引,而是返回一个不带有分组键的普通 DataFrame(或者 Series)。 例12-3

    **改动于 Pandas 1.5.0 :

    当使用 groupby 调用 applyby 参数生成分组结果时,并且结果行索引数量 和 groupby 对象分组数量 不匹配(不匹配则意味着无法汇总),则需要显式指定 group_keys 是否包含组键。

    **改动于 Pandas 2.0.0 :

    group_keys 默认为 True

observed 只显示观测值或显示所有值

  • observed: bool, default False

    观察值是指在实际数据中存在的唯一分类值。当应用 groupby 操作时,有时可能会遇到分类分组器中存在的分类值,但在实际数据中并未出现的情况。observed 参数允许你控制在分组操作中如何处理这些未观察到的分类值:

    • True: 只显示分类分组器(groupers)的观察值(observed values),而不显示未观察到的值。
    • False: 则显示所有分类分组器的可能值,包括未在实际数据中观察到的值。

    弃用于 Pandas 2.1.0 :

    自2.1.0版本以来已弃用:在panda的未来版本中,默认值将更改为True。

dropna 其他排序置

  • dropna: bool, default True 例13

    dropna 用于控制 groupby 对象的行数索引是否可以包含缺失值:

    • 如果为 True,并且组键包含缺失值,则将 缺失值与行/列一起删除。
    • 如果为 False,则保留缺失值。

    ⚠️ 注意 :

    舍弃缺失值的动作,是在分组前完成的,也就是说,在生成 groupby 分组对象的时候,就已经没有缺失值了。例13-2

相关方法:

➡️ 相关方法


  • resample

    Convenience method for frequency conversion and resampling of time series.

示例:

测试文件下载:

本文所涉及的测试文件,如有需要,可在文章顶部的绑定资源处下载。

若发现文件无法下载,应该是资源包有内容更新,正在审核,请稍后再试。或站内私信作者索要。

例1:如果没有指定聚合计算方法,分组结果将是一个 groupby 对象,只能通过 for 循环观察数据内容

  • 例1-1、准备演示数据
import pandas as pd# 读取一个演示文件df = pd.read_excel("../../../../数据集/团队成员季度销售额.xlsx")# 观察数据内容df.sample(5)
姓名片区1季度2季度3季度4季度year
18邹小琴华南40386053469111782023
4左美华华南5792944340873652023
84祝成云华东11863155197519222023
47紫薇华北17284802185769882023
40邹博文华北34345814133490612023
  • 例1-2、用 片区列 分组,但是不传递聚合计算方法
grouped = df.sample(5).groupby(by="片区")grouped

由上面结果可以发现,无法直接观察 GroupBy 对象

  • 例1-3、使用 for 循环观察分组内容
for group_name, group_data in grouped:print(f"Group: {group_name}")print(group_data)print("\n")
Group: 华中 姓名片区 1季度 2季度 3季度 4季度year73庄海彬华中2534 96841285454202359卓小珍华中32745837302579932023Group: 华北 姓名片区 1季度 2季度 3季度 4季度year95张华丽华北4584107230298976202348 紫湉华北30463918690864442023Group: 华南姓名片区1季度 2季度 3季度 4季度year97王娟华南6616784366086212023

例2:分组后,指定汇总计算方式,即可自动完成最终的合并过程,并生成新的 DataFrame

  • 例2-1、构建演示数据并观察数据内容
import pandas as pd# 读取一个演示文件df = pd.read_excel("../../../../数据集/团队成员季度销售额.xlsx")# 观察数据内容df.sample(5)
姓名片区1季度2季度3季度4季度year
71庄骏华中47132588648062242023
27邹立文华北15474927469385262023
38邹凤艳华北20555330646872292023
0左院梅华南14911083500094612023
77祝艳斌华东51611639129175282023
  • 例2-2、以片区列 作为分组依据,只传递一种求和的计算方法。
grouped = df.groupby(by="片区").sum()grouped
姓名1季度2季度3季度4季度year
片区
华东转身,泪倾城筑梦祝艳斌祝艳祝小娟祝仙花祝卫平祝玛拉初祝海英祝成云竹林听雨竹合竹猪哥传说诸子燕…5372062152771858527136414
华中梓英籽艺子鱼子墨子岚子和子菡资格卓越卓小珍卓向吴追影追忆追梦状之元巍笑吧庄臻庄永奇庄晓运庄…8624911993411950115388954621
华北邹美金邹灵美邹林华邹立文邹黎邹娟利邹杰邹建军邹建华邹吉宏邹积杰邹海利邹贵滨邹广坤邹凤艳邹昌乐…7277612546712000218655058667
华南左院梅左艳艳左薇左娜左美华左梅香左火英左儿左成娟醉霖~棉花糖最终幻想走向幸福邹邹邹子龙邹忠珠…608221096548934214389652598

以片区为分组依据,并传递了求和方法后,姓名列因为是字符串,所以相当于拼接。1季度、2季度、3季度、4季度、year等列,完成了求和计算。

  • 例2-3、指定计算方法,作为汇总方式,即可观察数据分组后的数据了。
grouped = df.groupby(by="片区").sum()grouped
姓名1季度2季度3季度4季度year
片区
华东转身,泪倾城筑梦祝艳斌祝艳祝小娟祝仙花祝卫平祝玛拉初祝海英祝成云竹林听雨竹合竹猪哥传说诸子燕…5372062152771858527136414
华中梓英籽艺子鱼子墨子岚子和子菡资格卓越卓小珍卓向吴追影追忆追梦状之元巍笑吧庄臻庄永奇庄晓运庄…8624911993411950115388954621
华北邹美金邹灵美邹林华邹立文邹黎邹娟利邹杰邹建军邹建华邹吉宏邹积杰邹海利邹贵滨邹广坤邹凤艳邹昌乐…7277612546712000218655058667
华南左院梅左艳艳左薇左娜左美华左梅香左火英左儿左成娟醉霖~棉花糖最终幻想走向幸福邹邹邹子龙邹忠珠…608221096548934214389652598
  • 例2-4、不同的列指定不同的汇总方式,没有指定汇总方式的列,不会出现在汇总结果。例如 姓名列。
grouped = df.groupby(by="片区").agg({"1季度": "max", "2季度": "mean", "3季度": "sum", "4季度": "min"}# 最大值# 平均值# 总和)# 最小值grouped
1季度2季度3季度4季度
片区
华东51613452.888889771851066
华中53084442.0000001195011684
华北45844326.4482761200021136
华南50704217.461538893421055
  • 例2-5、用于分组的分类数据,在完成数据分组后,会作为索引使用(行索引或列名,具体视分组方向而定)
grouped.axes
[Index(['华东', '华中', '华北', '华南'], dtype='object', name='片区'), Index(['1季度', '2季度', '3季度', '4季度'], dtype='object')]

例3:使用字典数据分组(直接把行索引里的值用字典的方式,指定为分组依据)

  • 例3-1、构建演示数据并观察数据内容
import pandas as pd# 读取一个演示文件df = pd.read_excel("../../../../数据集/团队成员季度销售额.xlsx")# 将 片区列,设置为索引df.set_index("片区", inplace=True)# 观察数据内容df.sample(5)
姓名1季度2季度3季度4季度year
片区
华东李先锋40574953677617232023
华南邹小琴40386053469111782023
华中子墨33931562360772732023
华北邹娟利38402815655132172023
华南邹秀珍32051772153469952023
  • 例3-2、 by 参数传入字典,字典的键是 DataFrame 行索引里的值,字典的值是分组名;
grouped = df.groupby(by={"华东": "东部战区", "华南": "南部战区", "华北": "北部战区", "华中": "中部战区"}).agg({"1季度": "max", "2季度": "mean", "3季度": "sum", "4季度": "min"}# 最大值# 平均值# 总和)# 最小值grouped
1季度2季度3季度4季度
片区
东部战区51613452.888889771851066
中部战区53084442.0000001195011684
北部战区45844326.4482761200021136
南部战区50704217.461538893421055

例4:使用Series数据分组(用Series替换当前行索引,并使用里面的值作为分组依据)

  • 例4-1、构建演示数据并观察数据内容
import pandas as pd# 读取一个演示文件df = pd.read_excel("../../../../数据集/团队成员季度销售额.xlsx")# 观察数据内容df.sample(5)
姓名片区1季度2季度3季度4季度year
60卓向吴华中47131691239892702023
43自在华北23544605592886142023
63追梦华中26896790424776372023
17邹秀珍华南32051772153469952023
42自在小英华北21002230340935722023
  • 例4-2、使用Series,构建数据分组。(为了方便,我们把片区列拿过来作为Series做演示)
# 提取片区列作为Seriess = df["片区"].copy(deep=True)# 使用Series,构建数据分组grouped = df.groupby(by=s).max()grouped
姓名片区1季度2季度3季度4季度year
片区
华东转身,泪倾城华东51616753677692842023
华中邹世军华中53087377720492702023
华北邹黎华北45847421694592302023
华南醉霖~棉花糖华南50707412597194612023

例5:使用函数数据分组(函数将作用于行索引的每个值)

  • 例5-1、构建演示数据并观察数据内容
import pandas as pd# 读取一个演示文件df = pd.read_excel("../../../../数据集/团队成员季度销售额.xlsx")# 将 片区列,设置为索引# df.set_index('片区',inplace=True)# 观察数据内容df.sample(5)
姓名片区1季度2季度3季度4季度year
38邹凤艳华北20555330646872292023
35邹海利华北38755799405783442023
83祝海英华东10181430484571552023
92邹世军华中43434866274376172023
11走向幸福华南43122189443174932023
  • 例5-1、当前行索引是自然索引,我想按照行索引的单数、双数进行分组,可以这样做:
# 定义区分单数双数的函数def rename_index(index):if index % 2 == 0:return "双数"else:return "单数"# 应用这个函数,处理行索引进行分组grouped = df.sample(12).groupby(by=rename_index)# 查看group对象里的内容for group_name, group_data in grouped:print(f"Group: {group_name}")print(group_data)print("\n")
Group: 单数姓名片区 1季度 2季度 3季度 4季度year37 邹广坤华北3015591221203750202377 祝艳斌华东5161163912917528202397王娟华南 661678436608621202389 诸子燕华东2454182433064198202379 祝小娟华东4419675328381066202311走向幸福华南4312218944317493202363追梦华中26896790424776372023Group: 双数 姓名片区 1季度 2季度 3季度 4季度year34邹积杰华北2175490248743110202392邹世军华中434348662743761720238 左成娟华南1747582314807025202376 筑梦华东1856390558086265202398 刘贤华东39606437314815172023

从上面这个结果可以发现,函数处理并没有影响到 groupby 对象

  • 例5-2、函数处理行索引的结果,会展现在汇总计算后,合并的新 DataFrame
# 给分组对象一个计算方式,完成最终数据合并,并观察grouped.max()
姓名片区1季度2季度3季度4季度year
单数邹广坤华南51616790443186212023
双数邹积杰华南43436437580876172023

从上面可以发现,如果 by 参数传递了函数,被修改的 行索引 只会作为分组依据、和分组名称,出现在汇总计算后,合并的新 DataFrame 里。

例6:使用pd.Grouper分组

  • 例6-1、构建演示数据并观察数据内容
from datetime import datetimeimport numpy as npimport pandas as pd# 创建一个包含时间序列的DataFramedate_rng = pd.date_range(start="2022-01-01", end="2022-01-19", freq="D")df = pd.DataFrame(date_rng, columns=["date"])# 添加一列随机数值df["value"] = np.random.randn(len(date_rng))# 观察数据内容df
datevalue
02022-01-010.632771
12022-01-021.218292
22022-01-03-0.864251
32022-01-040.628204
42022-01-05-0.625454
52022-01-061.021081
62022-01-070.685509
72022-01-081.096754
82022-01-09-1.131979
92022-01-100.384067
102022-01-110.447377
112022-01-120.005861
122022-01-131.126507
132022-01-14-0.153360
142022-01-150.447708
152022-01-160.470841
162022-01-17-1.143815
172022-01-18-0.407859
182022-01-190.308274
  • 例6-2、构建以‘周’为周期的grouper对象,并观察其数据内容
# 创建grouper对象grouper = pd.Grouper(key="date", freq="W")grouper
TimeGrouper(key='date', freq=, axis=0, sort=True, dropna=True, closed='right', label='right', how='mean', convention='e', origin='start_day')
  • 例6-3、按周进行分组,求每周的均值
# 按轴分组,并计算每组的均值result = df.groupby(grouper).mean()result
value
date
2022-01-020.925532
2022-01-090.115695
2022-01-160.389857
2022-01-23-0.414467

例7:by参数传递列名列表,构成多层索引,作为多维度的数据汇总

  • 例7-1、构建演示数据并观察数据内容
import pandas as pd# 读取一个演示文件df = pd.read_excel("../../../../数据集/团队成员日销售额.xlsx")# 只保留需要的列df = df[["职级", "片区", "业绩"]]# 观察数据内容df.sample(5)
职级片区业绩
82经理华中14494.9
14经理华南27318.5
67组长华东843.3
22经理华南853.8
43组长华北545.8
  • 例7-2、传递列名列表,多维度数据分组汇总。观察各职级销售人员,在不同地区的销售表现
df.groupby(by=["职级", "片区"]).sum()
业绩
职级片区
组长华东17526.0
华中4043.1
华北6524.8
经理华东148805.5
华中125646.0
华北58211.8
华南1067600.6
销售员华东7896.9
华中11233.6
华北1772.2
华南8281.1

例8:先转置再分割,实现类似纵向分割 axis=1 的效果

  • 例8-1、读取演示数据并观察内容
import pandas as pd# 读取一个演示文件df = pd.read_excel("../../../../数据集/团队成员日销售额_用于转置.xlsx")# 观察数据内容df
Unnamed: 001234567890919293949596979899
0职级销售员经理经理销售员销售员销售员销售员销售员销售员经理经理销售员组长销售员销售员销售员经理销售员销售员
1片区华南华南华南华南华南华南华南华南华南华中华中华中华中华中华中华中华中华中华中
2业绩523.916647.5825896.91051.1672.51542.2540.9752.558518261808968251112.9888.7721.3941.317740.2692.11165.2

3 rows × 101 columns

可以发现,在这个演示数据中,如果需要分组,则需要 axis=1 , 但是这不符合Pandas新版本特性。

  • 例8-2、先转置,再用片区分组
df.T.groupby(by=1).max()
02
1
华东销售员99327.4
华中销售员18261
华北销售员9388
华南销售员825896.9
片区职级业绩

分组完毕,by=1 是因为片区的哪一列,此时列名就是1

例9:多层索引需要使用 level 参数传递层级信息指定分组依据

  • 例9-1、读取演示数据,构建多层索引,观察数据内容
import pandas as pd# 读取一个演示文件df = pd.read_excel("../../../../数据集/团队成员日销售额.xlsx")# 只保留需要的列df = df[["职级", "片区", "业绩"]]# 构建多层索引df.set_index(["片区", "职级"], inplace=True)# 观察数据内容df.sample(5)
业绩
片区职级
华中经理1459.4
华南销售员540.9
销售员773.4
华中经理744.0
组长1304.8
  • 例9-2、 只使用片区作为分组依据,则只需要传递层级编号,或层级名称即可。
df.groupby(level="片区").sum()
业绩
片区
华东174228.4
华中140922.7
华北66508.8
华南1075881.7
df.groupby(level=0).sum()
业绩
片区
华东174228.4
华中140922.7
华北66508.8
华南1075881.7
  • 例9-3、 如果需要使用多列内容,使用列表传递层级编号或层级名称即可(可以混用)
df.groupby(level=[0, "职级"]).sum()
业绩
片区职级
华东组长17526.0
经理148805.5
销售员7896.9
华中组长4043.1
经理125646.0
销售员11233.6
华北组长6524.8
经理58211.8
销售员1772.2
华南经理1067600.6
销售员8281.1

例10:分组名称不再作为索引,使用SQL风格展示分组后的数据

import pandas as pd# 读取一个演示文件df = pd.read_excel("../../../../数据集/团队成员日销售额.xlsx")# 只保留需要的列df = df[["职级", "片区", "业绩"]]df# 用片区进行分组,并关闭索引返回df.groupby(by="片区", as_index=False).max()
片区职级业绩
0华东销售员99327.4
1华中销售员18261.0
2华北销售员9388.0
3华南销售员825896.9

由上面结果可以发现,片区列,没有再作为行索引。

例11:sort参数对分组结果的影响

  • 例11-1、默认情况下,数据分组后输出的 DataFrame 会开启组名排序
import pandas as pd# 构建演示数据df = pd.DataFrame({"cat": ["b", "b", "a", "a"], "value": [1, 3, 2, 4]})# 用cat列构建分组,保持分组名排序开启,grouped = df.groupby(by="cat").mean()grouped
value
cat
a3.0
b2.0
  • 例11-2、 当 sort=False 数据分组后输出的 DataFrame 不再对组名排序
# 用cat列构建分组,关闭分组名排序grouped2 = df.groupby(by="cat", sort=False).mean()grouped2
value
cat
b2.0
a3.0

例12:应用apply,如果结果行数 > 分组数量,则无法完成汇总,各分组的行索引(组键)会和分组名组成多层索引

  • 例12-1、首先来观察以下,各个分组的行索引
import pandas as pd# 构建演示数据df = pd.DataFrame({"cat": ["b", "b", "a", "a"], "value": [1, 3, 2, 4]})# 用cat列构建分组,保持分组名排序开启,grouped = df.groupby(by="cat")# 打印每个组的内容for name, group in grouped:print(f"Group {name}:")print(group)print("\n")
Group a:catvalue2 a23 a4Group b:catvalue0 b11 b3

留意上面结果,a和b两个分组的行索引2、3、0、1。

  • 例12-2、当 调用 apply ,但是结果行数 > 分组数量时,会产生由分组名、各分组行索引构成的多层索引,
import pandas as pd# 构建演示数据df = pd.DataFrame({"cat": ["b", "b", "a", "a"], "value": [1, 3, 2, 4]})# df['cat'] = df['cat'].astype('category')# 用cat列构建分组,保持分组名排序开启,grouped = df.groupby(by="cat").apply(lambda x: x)grouped
catvalue
cat
a2a2
3a4
b0b1
1b3
  • 例12-3、 当 group_keys=False 时,分组的键不会作为索引,而是返回一个不带有分组键的普通 DataFrame(或者 Series)。
import pandas as pd# 构建演示数据df = pd.DataFrame({"cat": ["b", "b", "a", "a"], "value": [1, 3, 2, 4]})# df['cat'] = df['cat'].astype('category')# 用cat列构建分组,保持分组名排序开启,grouped = df.groupby(by="cat", group_keys=False).apply(lambda x: x)grouped
catvalue
0b1
1b3
2a2
3a4
  • 例12-4、再来看一下,正常应该是什么样的
import pandas as pd# 构建演示数据df = pd.DataFrame({"cat": ["b", "b", "a", "a"], "value": [1, 3, 2, 4]})# df['cat'] = df['cat'].astype('category')# 用cat列构建分组,保持分组名排序开启,grouped = df.groupby(by="cat").apply(lambda x: x.mean())grouped
value
cat
a3.0
b2.0

例13:组键(分组名、或可理解为结果的行索引、也可以理解为各分组的行索引)缺失值处理

  • 例13-1 构建演示数据并观察
import pandas as pd# 构建演示数据l = [["a", 12, 12], [None, 12.3, 33.0], ["b", 12.3, 123], ["a", 1, 1]]df = pd.DataFrame(l, columns=["a", "b", "c"])df
abc
0a12.012.0
1None12.333.0
2b12.3123.0
3a1.01.0
  • 例13-2 在分组完成时,默认就已经舍弃了缺失值
grouped = df.groupby(by="a")# 打印每个组的内容for name, group in grouped:print(f"Group {name}:")print(group)print("\n")
Group a: a b c0a12.012.03a 1.0 1.0Group b: a bc2b12.3123.0

由上面结果可以发现,当完成分组的时候,就已经没有缺失值了,这一步发生在合并每个分组产生结果之前。

  • 例13-3 dropna=True 可以保留缺失值
grouped = df.groupby(by="a", dropna=False).mean()grouped
bc
a
a6.56.5
b12.3123.0
NaN12.333.0