- AI 技能
- 人工智能
- 深度研究
- 数据分析
- 媒体生成
【免费下载链接】SenseNova-Skills
Modular SenseNova skills for building AI-powered office assistants and productivity workflows
分组聚合(Group By)是 Excel 数据分析中出现频率最高的操作之一:将原始明细按分类维度汇总为频数、合计与占比,再配合可视化与格式化报告完成闭环交付。本文以 SenseNova-Skills 仓库中sn-da-excel-workflow工作流的group-by-analysis子能力为核心,完整讲解"多 Sheet 行数评估 → 大文件 Parquet 预处理 → 合并单元格/正则清洗 → 分组统计与占比 → 中文可视化 → 带样式与条件格式的 Excel 报告导出"的标准流水线。读完本文,你将能够在 AI 办公助手场景中直接复用这套可运行的代码模板,并对底层工作流编排机制形成清晰认知。
一、子能力定位:group-by-analysis 在工作流中的角色
group-by-analysis是sn-da-excel-workflow(Excel 数据分析多步编排器)中excel-data-analysis分类下的一个 capability 子 skill。父级工作流 sn-da-excel-workflow/SKILL.md 将其定位为覆盖"数据读取 → 行数评估 → 大文件优化 → 清洗 → 筛选 → 统计 → 导出"全流程的能力之一,并明确规定:遇到 Excel 分析 / 表格分析 / 分组统计 / 汇总统计 / 交叉分析 / 数据可视化 / 生成报表等触发词,或用户上传 .xlsx/.xls/.csv 文件时,应主动编排子 skill 完成分析,而不是手写几行 pandas 草率作答。
该子能力的官方描述(frontmatterdescription)概括了其覆盖范围:
对多 Sheet 的 Excel 文件进行行数统计、大文件 Parquet 转换预处理、数据清洗及分组聚合分析,并生成带样式标记的统计表与可视化图表。
从中可以提炼出四个核心职责,与文档中的四个 Step 一一对应:
- 数据清洗与预处理(合并单元格填充、正则清洗、分类映射);
- 分组聚合统计(频数、求和、占比、总计行);
- 可视化图表生成(中文字体、数值标签、网格美化);
- 带样式与条件格式的 Excel 报告生成与下载。
从源码结构看,excel-data-analysis目录下还并列存在 comparison-analysis、pivot-table-analysis、kpi-metric-analysis、time-series-analysis、trend-analysis 等兄弟子能力,它们共享同一套"行数评估 → 数据清洗 → 统计/可视化 → 导出"的骨架,group-by-analysis是其通用性最强、最基础的实现。
二、前置环节:多 Sheet 行数统计与大文件 Parquet 预处理
group-by-analysis面向多 Sheet Excel 文件,因此在进入清洗与聚合之前,必须先完成两件事:统计所有工作表的总行数,并依据数据量级决定读取策略。父级工作流给出了两条关键结论:
- 不要用
pd.read_excel()来数行数——它会把整个文件加载进内存,大文件下直接 OOM; - 读取策略由总行数决定,具体规则如下:
| 总行数 | 策略 | 操作 |
|---|---|---|
| < 10k | 直接读取 | df = pd.read_excel(file_path, sheet_name=target_sheet) |
| 10k – 100k | Parquet 缓存 | 首次pd.read_excel()后转df.to_parquet(),后续全部从 Parquet 读取 |
| >= 100k | 必须加载sn-da-large-file-analysisskill | 采用流式读取 + Parquet 模式,完全禁用pd.read_excel()(会 OOM 或超时) |
轻量级行数统计推荐使用 openpyxl 的read_only模式逐行计数,内存占用恒定:
import openpyxl, gc wb = openpyxl.load_workbook(file_path, read_only=True, data_only=True) total_rows = 0 sheet_info = {} for name in wb.sheetnames: ws = wb[name] row_count = sum(1 for _ in ws.iter_rows(min_row=2, values_only=True)) total_rows += row_count sheet_info[name] = row_count print(f"Sheet '{name}': {row_count} rows") wb.close() print(f"总行数={total_rows}")当总行数落在 10k–100k 区间时,采用 Parquet 中间缓存,避免同一文件的重复慢速读取:
import pandas as pd parquet_path = "/tmp/_auto_parquet.parquet" df = pd.read_excel(file_path, sheet_name=target_sheet) df.to_parquet(parquet_path, engine="pyarrow") del df; gc.collect() df = pd.read_parquet(parquet_path)对于 100k 行以上的文件,父级工作流强制要求先read_file加载 sn-da-large-file-analysis/SKILL.md,再使用其中的stream_excel_to_parquet():该函数基于 openpyxliter_rows以 5 万行为一个 chunk 流式写入 Parquet,内存占用保持恒定。同 skill 还提供了数值类型降级(downcast)与低基数字符串转 category 的内存优化手段,通常可节省 50%–80% 内存。这是group-by-analysis能够扩展到十万行级别数据的前提。
三、Step 1:数据清洗与预处理
Excel 手工表格最常见的三类脏数据问题是:合并单元格导致的分类列大面积空值、混入特殊字符的文本、以及含义相近但写法不统一的分类标签。group-by-analysis的 Step1 用三小段代码分别解决,并全部集中于目标分类列上:
import re # 1. 处理合并单元格:向前填充 target_col = 'category_column' df[target_col] = df[target_col].ffill() # 2. 正则清洗:去除无效字符或筛选特定格式 def clean_text(text): if pd.isna(text): return text return re.sub(r'[^\w\s]', '', str(text)).strip() df[target_col] = df[target_col].apply(clean_text) # 3. 分类映射函数骨架 def map_categories(value): mapping = { 'example_key_1': 'Group_A', 'example_key_2': 'Group_B' } return mapping.get(value, 'Others') df['group_tag'] = df[target_col].apply(map_categories)三点实现要点值得展开:
ffill()前向填充是处理合并单元格的标准手法。合并单元格在 pandas 读入后,仅首行有值、其余行均为 NaN,ffill()将上一行的分类值向下传递,还原每个明细行所属类别。兄弟子能力 missing-value-handling 与 grouped-statistics 均沿用了同一模式;后者还强调"ffill 前需确保数据按原始分类顺序排列",否则填充结果会错乱。- 正则清洗策略取决于业务语义。本例用
[^\w\s]剔除标点与符号;若目标是保留中文字符,则应改为re.findall(r'[\u4e00-\u9fff]', ...)提取纯中文,见 invalid-data-cleaning;若目标是去除 "1. " 之类的数字序号前缀,可参考 missing-value-handling 中的re.sub(r'^\d+[\.\s\-]+', '', text)。 - 分类映射函数骨架将零散原始值收敛为有限的标准分组标签,未命中项统一落入
'Others',这是保证后续groupby结果可读、可比的关键。bar-chart-visualization中类似的映射用法是combined_df[group_col].map(stage_mapping).fillna('其他'),可作对照参考。
大文件红线:当
total_rows >= 100k时,父级工作流明确禁止对整列使用df.apply(lambda...),应改用向量化操作或np.where()(详见 sn-da-large-file-analysis/SKILL.md 的向量化速查表)。
四、Step 2:分组聚合统计、占比与总计行
清洗完成后进入核心统计环节。group-by-analysis用 pandas 的groupby().agg()一次性完成计数与求和,再计算各分组在总和中的占比,最后拼上总计行形成完整的汇总表:
group_col = 'group_tag' value_col = 'value_column' # 分组聚合:计数与求和 summary = df.groupby(group_col)[value_col].agg(['count', 'sum']).reset_index() # 计算占比 total_sum = summary['sum'].sum() summary['percentage'] = (summary['sum'] / total_sum).map(lambda x: f"{x:.2%}") # 添加总计行 total_row = pd.DataFrame({ group_col: ['Total'], 'count': [summary['count'].sum()], 'sum': [total_sum], 'percentage': ['100.00%'] }) summary_final = pd.concat([summary, total_row], ignore_index=True) print(summary_final)这段模板的工程价值在于三点:
agg(['count', 'sum'])同时输出频数与求和,一个聚合调用覆盖"有多少条记录"和"合计数值多少"两类最常见指标;- 占比基于
sum而非count,语义上衡量的是数值贡献度而非记录数量,这与 category-statistics 中"数量占比"的视角形成互补——后者统计的是value_counts()后各类别行数占比; - 总计行单独构造再
concat,保证了合计值(count总和、sum总和、100.00%)由程序计算而非手工拼接,避免计算不一致。该"分组统计 + 总计行"的结构化输出,是后续写入 Excel 报告时的数据源,也是 pivot-table-analysis 中summary.loc['合计'] = summary.sum()的同类实践。
如果业务还需要"每行数值在所在分组内的占比"这类细分比例,可在分组内用transform实现,或直接参考percentage-calculation的逐行/列匹配提取方式,此处模板已覆盖最常见的分组级占比场景。
五、Step 3:生成可视化柱状图
统计表是理性结论,图表则是可读性结论。group-by-analysis的 Step3 使用 matplotlib 绘制分组求和柱状图,并完成三处必备美化:中文字体、数值标签、网格线:
import matplotlib.pyplot as plt # 配置中文字体支持 plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans'] plt.rcParams['axes.unicode_minus'] = False plt.figure(figsize=(10, 6), dpi=100) bars = plt.bar(summary[group_col], summary['sum'], color='#4472C4') # 添加数值标签 for bar in bars: height = bar.get_height() plt.text(bar.get_x() + bar.get_width()/2., height, f'{height:,.0f}', ha='center', va='bottom', fontsize=10) plt.title("Distribution Analysis", fontsize=14) plt.xlabel(group_col) plt.ylabel("Values") plt.grid(axis='y', linestyle='--', alpha=0.7) plt.tight_layout() chart_path = "analysis_chart.png" plt.savefig(chart_path)需要特别强调的是中文字体配置的正确姿势。直接在rcParams['font.sans-serif']里写['SimHei', 'DejaVu Sans']仅在系统已安装 SimHei 时有效;在无头容器化环境中,父级工作流 sn-da-excel-workflow/SKILL.md 提供了一段必须原样复制、禁止用fc-list/find fonts/pip install搜索字体的固定字体发现代码:它会按固定候选路径列表探测字体文件,找到后通过font_manager.addfont()注册并设为当前字体族,同时关闭坐标轴负号(unicode_minus)防止中文环境下负号显示为方块:
import os import matplotlib import matplotlib.pyplot as plt import matplotlib.font_manager as fm _FONT_PATHS = [ '/mnt/afs_agents/SimHei.ttf', '/mnt/afs_agents/mnt/data/SimHei.ttf', os.path.expanduser('~/.fonts/SimHei.ttf'), '/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc', '/usr/share/fonts/SimHei.ttf', ] for _p in _FONT_PATHS: if os.path.exists(_p): fm.fontManager.addfont(_p) matplotlib.rcParams['font.family'] = fm.FontProperties(fname=_p).get_name() break matplotlib.rcParams['axes.unicode_minus'] = False若需要更丰富的图形形态,可参照同目录兄弟子能力:分类占比场景可叠加饼图(pie-chart-visualization 中的环形图 + Base64 下载链接)、多分组对比可用双轴柱状图叠加占比曲线(category-statistics 的ax1.twinx()技巧),两类分类对比可用"柱状图 + 饼图"双子图布局(comparison-analysis)。高分辨率输出时统一使用dpi=300, bbox_inches='tight'并plt.close()释放内存。
六、Step 4:生成带样式与条件格式的 Excel 报告
最后一步是让分析结论"可交付":用 openpyxl 将summary_final写入 Excel,应用表头填充、字体、对齐、边框等样式,并对最大值所在行做绿色高亮,最后自动调整列宽并输出下载链接:
from openpyxl import Workbook from openpyxl.styles import PatternFill, Font, Alignment, Border, Side output_path = "analysis_report.xlsx" wb = Workbook() ws = wb.active ws.title = "Summary Report" # 定义样式 header_style = { "fill": PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid"), "font": Font(bold=True, color="FFFFFF"), "alignment": Alignment(horizontal="center"), "border": Border(left=Side(style="thin"), right=Side(style="thin"), top=Side(style="thin"), bottom=Side(style="thin")) } highlight_style = PatternFill(start_color="00B050", end_color="00B050", fill_type="solid") # 写入数据并应用样式 for r_idx, row in enumerate(summary_final.values, 2): for c_idx, value in enumerate(row, 1): cell = ws.cell(row=r_idx, column=c_idx, value=value) # 示例:对最大值所在行进行绿色标记 if value == summary['sum'].max(): cell.fill = highlight_style # 自动调整列宽 for col in ws.columns: max_length = max(len(str(cell.value)) for cell in col) ws.column_dimensions[col[0].column_letter].width = max_length + 2 wb.save(output_path) print(f"Download link: {output_path}")这段导出代码体现了几条可复用的 openpyxl 实践:
- 样式以字典形式组织(fill/font/alignment/border),便于一次定义、多处复用;配色沿用工作流惯用的品牌蓝
4472C4表头与高亮绿00B050,与 grouped-statistics、threshold-cell-coloring 等兄弟能力保持视觉一致; - 条件格式通过遍历单元格判断值实现:这里以"数值等于分组求和最大值"作为绿色填充触发条件,逻辑上等价于"高亮贡献最大的分组"。若需要更复杂规则(如整行低于均值标绿、整行标红),可参考 threshold-cell-coloring 的"先算均值、再按布尔列决定是否填充"模式和 formatted-export 的"逐行逐列标红"模式;
- 列宽按内容自适应:对每一列取最长单元格字符串长度 +2 作为宽度,避免中文或长数值被截断;
- 下载链接按运行环境灵活输出:本地环境打印文件路径,Notebook 环境可用
FileLink(见 comparison-analysis),Web 服务环境则可借助file_service.get_download_url换取临时可访问 URL(见 bar-chart-visualization)。
若报告还需要嵌入图表,可参考 pie-chart-visualization:把 matplotlib 图表写入BytesIO,再通过openpyxl.drawing.image.Image将图片锚定到指定单元格,实现"统计表 + 图表"一体的 Excel 报告。
七、全流程编排与最佳实践总结
把四个 Step 串联起来,group-by-analysis的完整调用链是:
- 行数评估(openpyxl
read_only流式计数)→ 2.读取策略决策(<10k 直接读;10k–100k 转 Parquet;>=100k 加载 sn-da-large-file-analysis/SKILL.md)→ 3.Schema 探查(nrows=20只读样本,绝不全量加载)→ 4.清洗(ffill / 正则 / 分类映射)→ 5.分组统计(count + sum + 占比 + 总计行)→ 6.可视化(固定字体路径 + 柱状图数值标签)→ 7.报告导出(openpyxl 样式 + 条件高亮 + 下载链接)。
最后给出几条贯穿始终的最佳实践,均来自父级工作流与兄弟能力的明确约定:
- 先数行数再决定策略:一切大文件判断都以 10k / 100k 两级阈值为准,绝不先
pd.read_excel()全量加载; - 列名可能包含空格(如
'是否通 过'),务必使用精确字符串索引而非属性访问; - 无表头 Sheet 用
header=None+ 位置索引; - 禁止在生成环境中搜索字体或安装包:固定字体路径块原样复制,禁止
fc-list、find fonts、pip install; - 大文件禁止
df.apply(lambda...)、df.iterrows()与打印全量数据:改用向量化操作、itertuples()与.head()/.value_counts().head(); - 及时释放内存:每个中间 DataFrame 处理完毕后
del df; gc.collect()。
通过本文给出的完整代码模板与工作流上下文,开发者可以在 SenseNova-Skills 的 AI 办公助手场景中直接落地一套可运行、可扩展的分组聚合分析流水线;需要更细粒度实现时,可继续查阅 父级工作流 SKILL.md 及各 capability 子 skill,按需加载对应数据清洗、筛选、可视化与导出模式。
- AI 技能
- 人工智能
- 深度研究
- 数据分析
- 媒体生成
【免费下载链接】SenseNova-Skills
Modular SenseNova skills for building AI-powered office assistants and productivity workflows
相关推荐
SenseNova-Skills 分类对比分析实战:基于 sn-da-excel-workflow 的 Excel 双类别差异统计与可视化指南
SenseNova Skills 分类对比分析实战:基于 sn da excel workflow 的 Excel 双类别差异统计与可视化指南 本文是 Sens
AI 技能人工智能深度研究数据分析媒体生成SenseNova-Skills 数据分析技能实战指南:从 Excel 多表清洗到大文件流式分析与图像数据提取
SenseNova Skills 数据分析技能实战指南:从 Excel 多表清洗到大文件流式分析与图像数据提取 SenseNova Skills 仓库内置了三个
AI 技能人工智能深度研究数据分析媒体生成Open-Meteo:零密钥的免费天气 API,一条 curl 拿全球预报
Open Meteo:零密钥的免费天气 API,一条 curl 拿全球预报 Open Meteo 是一个用 Swift 编写的开源气象数据服务:它把 NOAA
后端API网关数据工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考