Python 数据处理入门(八)分组聚合:Excel 数据透视表的代码版
写在前面上一篇我们把脏数据收拾干净了类型统一、重复行删掉、异常值也标了出来。数据干净之后下一步就是回答老板最关心的那句——“按城市、按产品分别是多少”这就是分组聚合Excel 里叫数据透视表。用鼠标拖字段当然能做但每周都要重做一遍就很烦。这篇我们把数据透视表的每一个动作翻译成 groupby 的代码单列分组、多列分组、一次算多个统计量、给结果列起中文名、算出每组里最大的那一单、以及组内占比。学完这一篇社团统计报名人数、课程设计算平均分、工作里做月度报表都是同一套写法。先保证示例数据在手边第 1 篇的 mkdata.pyimport osimport mkdata # 第 1 篇里的 mkdata.py​if not os.path.exists(“data/sales.csv”):mkdata.main() # 没生成过就先跑一次print(“示例数据就绪”, os.path.exists(“data/sales.csv”))这段会检查第 1 篇生成的 data/ 目录在不在不在就自动补一次已经有数据时只会打印一行 示例数据就绪 True不影响后面的结果。一、groupby 的世界观拆分—计算—合并按「城市 × 产品」两个维度分组的销售额就是 groupby 的直接成果——Excel 数据透视表也是同一张图。先看这张销售明细表。它一共 16 行城市列里有空值金额列里也有空值这正是真实报表的样子import pandas as pd​df pd.read_csv(“data/sales.csv”, encoding“utf-8-sig”)print(df.head(4).to_string(indexFalse))print(“形状”, df.shape)订单日期 城市 产品 数量 单价 销售员 金额2024-01-05 杭州 A-笔记本 3 49.9 小张 149.72024-01-06 上海 B-鼠标 5 29.9 小李 149.52024-01-06 上海 B-鼠标 5 29.9 小李 149.52024-01-09 北京 C-键盘 2 89.0 小王 178.0形状 (16, 7)groupby(“城市”) 做的事可以拆成三步拆分split把 16 行按城市切成 杭州、上海、北京、广州 等几堆计算apply每一堆各自算你指定的统计量合并combine把每堆的结果拼回一张表城市变成索引。所以 df.groupby(“城市”) 单独写在代码里是没有结果的它只是个待计算的中间对象必须再跟一个聚合函数sum / mean / size ……才会出数。二、单列分组size、count、sum、mean 到底差在哪import pandas as pd​df pd.read_csv(“data/sales.csv”, encoding“utf-8-sig”)print(“— 订单数size”)print(df.groupby(“城市”, dropnaFalse).size())print(“— 有金额的订单数count”)print(df.groupby(“城市”, dropnaFalse)[“金额”].count())— 订单数size城市上海 4北京 3广州 3杭州 5NaN 1dtype: int64— 有金额的订单数count城市上海 4北京 2广州 2杭州 5NaN 1Name: 金额, dtype: int64这里有两个新手最容易混淆的点size 数的是行数count 数的是非空值个数。上海 4 单但金额有值的只有 4 个上海这组刚好没有缺失广州 3 单却只有 2 个有金额说明广州那组有一行金额是空的。做订单量报表用 size做有效金额笔数用 count。dropnaFalse 一定要加。城市列为空的那一行默认会被 groupby 悄悄丢掉dropnaTrue 是默认值报表里就少了 149.7 元。加了 dropnaFalse空分组会以 NaN 作为组名保留下来方便你回头去补数据。接着把常用的统计量一次看全import pandas as pd​df pd.read_csv(“data/sales.csv”, encoding“utf-8-sig”)print(df.groupby(“城市”, dropnaFalse)[“金额”].agg([“count”, “sum”, “mean”, “median”, “max”, “min”]).round(2).to_string())count sum mean median max min城市上海 4 1545.0 386.25 386.25 623.0 149.5北京 2 227.9 113.95 113.95 178.0 49.9广州 2 535.4 267.70 267.70 356.0 179.4杭州 5 2805.1 561.02 149.70 2995.0 -599.0NaN 1 149.7 149.70 149.70 149.7 149.7注意杭州的 min 是 -599.0、max 是 2995.0——金额列里既有一条录入错误的负数也有一条漏算了单价的异常大值。这就是为什么上一篇要先清洗聚合函数不会帮你判断数据对不对它只会老实求和。平均值被这条负数拉低中位数 149.7 反而更能代表杭州的典型客单价。三、多列分组一张二维表一个 groupby agg 同时算出订单数、销售额、客单价三个指标不必写三遍分组代码。只按城市分组太粗通常还要看哪个城市卖哪个产品好。groupby 传一个列表就行import pandas as pd​df pd.read_csv(“data/sales.csv”, encoding“utf-8-sig”)result (df.groupby([“城市”, “产品”], dropnaFalse).agg(销售额(“金额”, “sum”), 有效订单数(“金额”, “count”)).reset_index().sort_values(“销售额”, ascendingFalse))print(result.to_string(indexFalse))城市 产品 销售额 有效订单数杭州 D-显示器 2396.0 2上海 C-键盘 1246.0 2广州 C-键盘 356.0 1杭州 A-笔记本 349.3 2上海 B-鼠标 299.0 2广州 B-鼠标 179.4 1北京 C-键盘 178.0 1NaN A-笔记本 149.7 1杭州 B-鼠标 59.8 1北京 A-笔记本 49.9 1北京 D-显示器 0.0 0三件事值得说明多列分组的结果是 MultiIndex多层索引正常显示时会分两行列头。reset_index() 把城市和产品还原成普通列变成一张能直接导出 Excel 的平表——报表交付前基本都要这一步。agg(销售额(“金额”, “sum”)) 是命名聚合。等号左边是新列名可以写中文右边是源列名, 函数名的元组。这是 pandas 里最好用的聚合写法不用改列名、不用处理多层列头。老写法 agg({“金额”: “sum”}) 用的是字典语义是对金额列做 sum但列名没法自定义多函数时还会产生两层列头。两种都能用新代码建议直接上命名聚合。北京 D-显示器 的销售额是 0.0、有效订单数 0因为那一行金额是空的。sum 把全空组算成 0count 算成 0而 size 会告诉你这里确实有 1 行数据——三个函数放在一起看才不会误判。四、取每组最大/最小的那一行透视表只能给你一个数字但业务上更常问的是每个城市卖得最贵的那一单是谁买的。这时要用 idxmax 拿到最大值所在的行标签再用 loc 取回整行import pandas as pd​df pd.read_csv(“data/sales.csv”, encoding“utf-8-sig”)idx df.groupby(“城市”, dropnaFalse)[“金额”].idxmax()print(df.loc[idx, [“订单日期”, “城市”, “产品”, “数量”, “金额”]].to_string(indexFalse))订单日期 城市 产品 数量 金额2024-03-15 上海 C-键盘 7 623.02024-01-09 北京 C-键盘 2 178.02024-05-06 广州 C-键盘 4 356.02024-05-20 杭州 D-显示器 5 2995.02024-04-18 NaN A-笔记本 3 149.7idxmax 返回的是原表的行号df.loc[行号列表] 就能把这些行整行捞出来——这是分组后取 Top1最省事的写法。如果不想记 idxmax第二种写法是先排序再去重import pandas as pd​df pd.read_csv(“data/sales.csv”, encoding“utf-8-sig”)top1 (df.sort_values(“金额”, ascendingFalse).drop_duplicates(subset[“城市”], keep“first”).sort_values(“城市”))print(top1[[“城市”, “产品”, “金额”]].to_string(indexFalse))城市 产品 金额上海 C-键盘 623.0北京 C-键盘 178.0广州 C-键盘 356.0杭州 D-显示器 2995.0NaN A-笔记本 149.7两种写法结果一致区别在drop_duplicates 依赖排序遇到金额相同的两行时保留的是排序后靠前的那一行结果不完全确定idxmax 遇到并列最大值会报 ValueError逼你明确处理并列情况。数据量大时优先 idxmax它不用把整张表排序要取每组前 3 名这种需求则用 df.sort_values(…).groupby(“城市”).head(3)。五、transform把组内占比贴回每一行agg 的返回结果是每组一行行数和原表对不上没法直接加回原表。想要这一单占它所在城市总额的百分之几要用 transformimport pandas as pd​df pd.read_csv(“data/sales.csv”, encoding“utf-8-sig”)df[“城市销售额”] df.groupby(“城市”, dropnaFalse)[“金额”].transform(“sum”)df[“占城市比重%”] (df[“金额”] / df[“城市销售额”] * 100).round(1)print(df[[“城市”, “产品”, “金额”, “城市销售额”, “占城市比重%”]].to_string(indexFalse))城市 产品 金额 城市销售额 占城市比重%杭州 A-笔记本 149.7 2805.1 5.3上海 B-鼠标 149.5 1545.0 9.7上海 B-鼠标 149.5 1545.0 9.7北京 C-键盘 178.0 227.9 78.1杭州 A-笔记本 199.6 2805.1 7.1广州 B-鼠标 NaN 535.4 NaN广州 B-鼠标 179.4 535.4 33.5北京 A-笔记本 49.9 227.9 21.9上海 C-键盘 623.0 1545.0 40.3上海 C-键盘 623.0 1545.0 40.3杭州 B-鼠标 59.8 2805.1 2.1NaN A-笔记本 149.7 149.7 100.0北京 D-显示器 NaN 227.9 NaN广州 C-键盘 356.0 535.4 66.5杭州 D-显示器 -599.0 2805.1 -21.4杭州 D-显示器 2995.0 2805.1 106.8transform 和 agg 的唯一区别就是返回长度agg 给你每组一个值transform 给每一行都填上它所属组的那个值。所以 df[“城市销售额”] 的长度和 df 完全一致可以直接做除法不会出现赋值后一堆 NaN的经典事故。输出里杭州 D-显示器 那两条特别有教学意义单价 599 的那条占 106.8%被误录成 -599 的那条是 -21.4%两条加起来正好 85% 左右——占比超过 100% 或为负数就是数据有问题的信号。六、pivot_table和 groupby 是一回事如果 Excel 的透视表用惯了可以直接用 pd.pivot_table它内部就是 groupby unstackimport pandas as pd​df pd.read_csv(“data/sales.csv”, encoding“utf-8-sig”)print(“— pivot_table 版本”)print(pd.pivot_table(df, index“城市”, columns“产品”, values“金额”, aggfunc“sum”, dropnaFalse).round(1).to_string())print(“— groupby unstack 版本”)print(df.groupby([“城市”, “产品”], dropnaFalse)[“金额”].sum().unstack(fill_value0).to_string())— pivot_table 版本产品 A-笔记本 B-鼠标 C-键盘 D-显示器城市上海 NaN 299.0 1246.0 NaN北京 49.9 NaN 178.0 0.0广州 NaN 179.4 356.0 NaN杭州 349.3 59.8 NaN 2396.0NaN 149.7 NaN NaN NaN— groupby unstack 版本产品 A-笔记本 B-鼠标 C-键盘 D-显示器城市上海 0.0 299.0 1246.0 0.0北京 49.9 0.0 178.0 0.0广州 0.0 179.4 356.0 0.0杭州 349.3 59.8 0.0 2396.0NaN 149.7 0.0 0.0 0.0对应关系记这一句就够了Excel 透视表 Pandas行标签 index / groupby 的第一个键列标签 columns / groupby 的第二个键 unstack()值 values值字段设置求和/计数/平均 aggfunc / agg两个版本输出几乎一样唯一差别是 unstack(fill_value0) 把没有这个组合填成了 0而 pivot_table 默认留 NaN。0 和 NaN 是两种含义0 表示卖了但金额加起来是 0NaN 表示压根没有这个组合。做报表时想清楚要哪一种别让 0 把没数据掩盖掉。pivot_table 的 aggfunc 也支持一次传多个这时列头会变成两层同样用命名聚合或 reset_index 后的扁平结构更好读。常见问题结果里的城市变成了列头怎么变回普通列说明你拿到的是带索引的 Series 或 DataFrame。加 .reset_index() 即可。多列分组时它会一次还原成两列。分组后有一行组名是 NaN是 bug 吗不是。要么是你加了 dropnaFalse要么是分组列本身有空值。想彻底消失就先用 fillna(“未知”) 把空值填掉再分组报表里显示未知比显示空更清楚。报错 AttributeError: ‘DataFrame’ object has no attribute ‘append’把两个汇总表上下摞起来不要用 append这个方法在 pandas 2.0 就已经从 DataFrame 上移除了改用 pd.concat([表1, 表2], ignore_indexTrue) 或者直接 reset_index() 后合并。下一篇会专门讲。小结这一篇把 Excel 数据透视表完整翻译成了代码groupby 负责拆分组、size/count/sum/mean 负责算、命名聚合负责起名字、reset_index 负责变成能导出的平表、transform 负责把组内统计贴回每一行、pivot_table 负责交叉表。三个必须记住的坑分组列有空值会整行消失记得 dropnaFalse、size 和 count 数的是不同的东西、聚合前先清洗否则一条 -599 的错数据会把整个城市的平均值拉偏。下一篇我们聊表的合并与拼接手上有订单表和客户表两张表怎么按客户 ID 把它们对起来inner / left / right / outer 四种连接有什么区别为什么 merge 之后行数会变多以及上下拼接新一批数据时该怎么写。

相关新闻

WMSST结合MCNN-GRU网络故障诊断的Matlab实现

WMSST结合MCNN-GRU网络故障诊断的Matlab实现

1. 为什么盯上网络故障诊断这个方向网络故障诊断这事儿,我入行这些年深有体会——它看起来是个“不出彩”的工程问题,但真做起来,坑比想象中深得多。现代网络的规模早就超出了人工排查能承受的极限,核心网、数据中心、边缘接入层层…

2026/10/19 12:07:20 阅读更多 →
可穿戴式视觉-运动-触觉同步观测系统

可穿戴式视觉-运动-触觉同步观测系统

1. 项目概述:这不是一个“机器人”,而是一套人体运动感知的显微镜系统 你有没有试过在厨房里单手开罐头,另一只手还端着刚煮好的面?或者在狭窄的维修空间里,一边用扳手拧螺丝,一边用指尖确认零件卡扣是否到…

2026/10/19 12:07:20 阅读更多 →
两个-I都能编译成功,结果却不同:沿TinyCC的TCCState追一次头文件查找

两个-I都能编译成功,结果却不同:沿TinyCC的TCCState追一次头文件查找

我想研究TinyCC,最主要的原因是它小,感觉自己能读得下去。打开源码以后,还是会遇到一个很大的结构体:TCCState。里面有头文件路径、库路径、用户选项,也有编译过程中留下的信息。 如果从第一个字段开始逐个看&#xf…

2026/10/19 12:06:19 阅读更多 →

最新新闻

HTTPS加密过程详解:从TLS握手到密钥协商与排障实战

HTTPS加密过程详解:从TLS握手到密钥协商与排障实战

前阵子某公司同事拿了个抓包文件来找我,说某个内部系统的接口访问时好时坏,偶尔卡住十几秒然后报错。我打开抓包软件扫了一眼,发现大量TLS握手中的重传报文,再往下看,原来他压根没把HTTPS加密过程当回事——以为是“握…

2026/10/19 13:01:18 阅读更多 →
Agentic AI落地社会价值:提示工程架构师如何拆解信任与责任难题

Agentic AI落地社会价值:提示工程架构师如何拆解信任与责任难题

Agentic AI的社会价值最近被反复讨论,但真正落到实际业务场景里,落地障碍往往不是模型能力,而是隐藏在提示工程里的一系列细节。作为一个长期和Agent打交道的提示工程架构师,我越来越清楚一件事:我们写的每一段Prompt&…

2026/10/19 13:01:18 阅读更多 →
售货机管理系统设计:设备状态、库存预警与远程指令三重闭环

售货机管理系统设计:设备状态、库存预警与远程指令三重闭环

简介:本资源是一套基于RuoYi框架(SpringBootVue前后端分离)开发的售货机管理系统,面向Java全栈初学者、Web应用开发者及后台管理平台实践者,聚焦智能硬件运营场景中的商品、库存、设备、支付与销售数据等核心管理需求。…

2026/10/19 13:01:18 阅读更多 →
CMSSW离线数据处理框架:从LHC原始数据到物理分析的全链路实践

CMSSW离线数据处理框架:从LHC原始数据到物理分析的全链路实践

简介:CMSSW(Compact Muon Solenoid Software)是CERN大型强子对撞机CMS实验核心的开源离线数据分析框架,面向粒子物理研究人员、高能物理方向研究生及计算科学开发者,用于处理海量探测器数据、执行事件重建、物理对象识…

2026/10/19 13:01:17 阅读更多 →
ROS 2中VS Code无法识别rclcpp?从环境变量到CMake配置全排查

ROS 2中VS Code无法识别rclcpp?从环境变量到CMake配置全排查

前阵子在技术群里看到一位刚转到ROS 2的同行提问&#xff1a;新建的rclcpp项目在VS Code里打开&#xff0c;#include <rclcpp/rclcpp.hpp>这一行直接画了红色波浪线&#xff0c;代码提示完全失灵&#xff0c;尝试编译又报出find_package找不到rclcpp包的错误。这个问题我…

2026/10/19 13:01:17 阅读更多 →
C标准库源码深度剖析:从printf到malloc的底层实现与移植指南

C标准库源码深度剖析:从printf到malloc的底层实现与移植指南

简介&#xff1a;这份资源是C标准库的完整源代码集合&#xff0c;面向希望深入理解C语言底层实现、提升系统级编程能力的开发者与学习者。它涵盖输入输出、字符串处理、内存管理、数学运算、时间日期、文件系统接口等核心模块&#xff0c;适合在阅读源码、优化性能或进行底层开…

2026/10/19 13:00:15 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介&#xff1a;本资源为电源技术方向的学术研究文档&#xff0c;聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略&#xff0c;面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址&#xff1a; https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集&#xff0c;共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →