前言透视表pivot table是把长表转成交叉表的经典操作 用一列做行标签、一列做列标签、一列做取值交叉的格子里放聚合结果。 Excel 里的透视表人人会点pandas 里对应的是pivot_table。初学者最容易犯的两个错一是把pivot和pivot_table当成一回事 二是以为不传聚合函数就是求和。实际情况是pivot只做重塑、不聚合 遇到重复的行×列组合会直接报错而pivot_table默认的聚合函数是平均值mean不是求和。需要说明pandas 是第三方库本机没有 Python 解释器也没有 pandas 示例无法运行验证只能逐行人工推演。参数细节以 Pandas 官方文档为准。一、pivot_table 的参数官方签名以当前文档为准pandas.pivot_table(data, valuesNone, indexNone, columnsNone,aggfuncmean, fill_valueNone, marginsFalse,dropnaTrue, margins_nameAll, observedTrue,sortTrue, **kwargs)参数作用data输入DataFramevalues要聚合的数值列index放到行上的分组键columns放到列上的分组键aggfunc聚合函数默认meanfill_value结果里缺失填什么margins是否加合计行列理解方式index和columns是分组维度values是被聚合的度量aggfunc决定怎么聚。它和groupby是一体的——pivot_table基本可以理解为 按index和columns分组后对values做aggfunc再把结果摊开。 参数里dropnaTrue指的是分组键含缺失的行会被丢弃 和后面讲的结果表里的空位不是一回事别混淆。二、最基础的例子# pandas 1.0import pandas as pddf pd.DataFrame({region: [东, 东, 南, 南, 东],month: [1月, 2月, 1月, 2月, 1月],amount: [10, 20, 30, 40, 50],})table pd.pivot_table(df,valuesamount,indexregion,columnsmonth,aggfuncsum,fill_value0,marginsTrue, # 加 All 合计行列)print(table)注意这里显式写了aggfuncsum。如果不写结果会是均值 这是与直觉最不一致的地方务必记住。三、多个聚合函数、多列取值aggfunc可以传列表values也可以传列表# pandas 1.0table pd.pivot_table(df,valuesamount,indexregion,columnsmonth,aggfunc[sum, count],)这样会产生外层是聚合函数、内层是月份的列层级MultiIndex的列。 当values和aggfunc都是列表时列层级会更多一层 取值时要用元组逐级定位例如table[(sum, 1月)]。四、fill_value 与 marginsfill_value填的是结果表里的空位。因为不是每种维度组合都有数据交叉后必然出现空格填 0 或填其他默认值能让表更整齐。marginsTrue会增加All行和All列值是对应的汇总。汇总用的函数由aggfunc决定多函数时行为要按文档确认。margins_name可改合计行列的名字默认是All。五、pivot 与 pivot_table 的区别对比pivotpivot_table是否聚合不聚合只重塑会聚合重复组合报错用aggfunc合并默认聚合无mean适用已经是唯一组合的整洁数据有重复、需要汇总的数据pivot的语义很直接indexcolumns必须唯一确定一个values 否则就抛出ValueError: Index contains duplicate entries。 遇到这个报错第一反应应该是改用pivot_table并指定aggfunc。六、crosstab另一条做交叉表的路如果只是想数两个类别交叉出现多少次pd.crosstab更顺手# pandas 1.0freq pd.crosstab(df[region], df[month])print(freq)crosstab默认做的是计数频数它接受的是类别的序列而不是整张表 语义上和透视表互补。它还支持normalize参数用来把频数换算成比例或百分比 例如按行、按列或整体归一做占比分析时很方便。七、把结果整理成平表透视表的结果通常带着行索引和列索引可能是多层直接写 Excel 会挤在一起 不适合再加工。常见的整理方式是压平列名 重置索引# pandas 1.0flat table.reset_index()flat.columns [_.join(map(str, col)) if isinstance(col, tuple) else str(col)for col in flat.columns]print(flat)reset_index()把行索引变回普通列列名若是多层元组就拼成一个字符串 得到的就是一张规规矩矩的平表可以直接交给to_excel或下游系统。如果只是想快速看结构table.stack()/table.unstack()能在 宽表和长表之间切换但stack后缺失值默认会被丢掉 需要保留请显式传dropnaFalse。八、完整示例# pandas 1.0import pandas as pddef report(rows: pd.DataFrame) - pd.DataFrame:table pd.pivot_table(rows,valuesamount,indexregion,columnsmonth,aggfuncsum,fill_value0,marginsTrue,margins_name合计,)return table.sort_values(合计, ascendingFalse)if __name__ __main__:data pd.DataFrame({region: [东, 南, 东],month: [1月, 1月, 2月],amount: [10, 30, 20],})print(report(data))常见坑点以为pivot_table默认求和❌ 不写aggfunc直接调用得到的是平均值和报表预期不符 ✅ 求和就写aggfuncsum求计数写count或size用pivot处理有重复组合的数据❌df.pivot(indexa, columnsb, valuesc)抛重复条目错误 ✅ 改pivot_table(..., aggfuncsum)让重复组合被聚合掉用之类的判等去找透视表里的空值❌ 结果表里的空位当成普通缺失用比比不出来 ✅ 生成时就传fill_value0或事后用isna()判定fill_value和原始数据的填充搞混❌ 以为fill_value会改原始DataFrame✅fill_value只作用于生成的透视表原表不变多函数聚合后按列名直接取列❌table[1月]报 KeyError因为列已经变成多层索引 ✅ 用元组定位table[(sum, 1月)]或先table.columns看层级aggfunccount与size混用❌ 想数行数却用count结果NaN没被算进去数目偏小 ✅count数非缺失值size数行数含缺失按需求选分组维度里有分类类型时列爆炸❌ 某个键是category类型且基数很大透视表出几百列且大量为空 ✅ 注意observed参数为True时只显示出现过的组合避免空组合撑爆列总结需求该怎么做交叉汇总pivot_table(values, index, columns, aggfunc)默认行为aggfunc是mean不是sum填空白fill_value加合计marginsTrue只重塑不聚合pivot要求组合唯一只数频数pd.crosstab透视表的本质是分组 摊开。只要记住index/columns是维度、values是度量、aggfunc默认是均值绝大多数困惑都能避免。 真正动手前建议先在数据上跑一次groupby确认口径再换pivot_table摊平。