Pandas 表格布局重塑实战:从排序到 pivot / pivot_table / melt 的长宽格式转换
2026/9/18 21:30:54 网站建设 项目流程

Pandas 表格布局重塑实战:从排序到 pivot / pivot_table / melt 的长宽格式转换

【免费下载链接】pandasFlexible and powerful data analysis / manipulation library for Python, providing labeled data structures similar to R data.frame objects, statistical functions, and much more项目地址: https://gitcode.com/gh_mirrors/pa/pandas

本教程源自 pandas 官方入门系列《10 minutes to pandas》中的「How to reshape the layout of tables」,以 titanic.csv 与 air_quality_long.csv 两份真实数据集为线索,系统讲解表格行排序、长表转宽表(pivot)、带聚合的透视表(pivot_table)以及宽表转长表(melt)四类最常用的表格布局重塑操作。读完本文,你将能够熟练地在长格式(long/tidy format)与宽格式(wide format)之间自由切换数据表布局,并理解其底层源码实现逻辑。

本文所有示例代码均可在 pandas 源码仓库的 pandas/core/frame.py、pandas/core/reshape/pivot.py 与 pandas/core/reshape/melt.py 中找到对应的实现依据。

教程数据集准备

本教程使用两个数据集,均位于仓库的doc/data/目录下。

泰坦尼克号乘客数据(titanic.csv)

import pandas as pd titanic = pd.read_csv("doc/data/titanic.csv") titanic.head()

这份数据包含每位乘客的船舱等级(Pclass)、姓名、性别、年龄(Age)、票价(Fare)、是否幸存等字段,用于演示表格行排序

空气质量长表数据(air_quality_long.csv)

空气质量数据来自 OpenAQ 项目,记录了巴黎(FR04014)、安特卫普(BETR801)与伦敦(London Westminster)三个测量站点的NO₂PM2.5浓度值,用于演示长表与宽表之间的相互转换

air_quality = pd.read_csv( "doc/data/air_quality_long.csv", index_col="date.utc", parse_dates=True ) air_quality.head()

该数据集的列结构如下:

  • city:传感器所在城市(Paris / Antwerp / London)
  • country:传感器所在国家(FR / BE / GB)
  • location:传感器 ID(FR04014 / BETR801 / London Westminster)
  • parameter:被测参数(no2或 Particulate matter)
  • value:测量值
  • unit:测量单位(µg/m³)

DataFrame 的索引为datetime,即测量时刻。

说明:该空气质量数据以长格式(long format)存储——每个观测值占据独立的一行,每个变量占据独立的一列。长格式也被称为 tidy data format,是数据科学中最推荐的存储形态之一。

排序表格行:sort_values

按单个列排序

需求:按乘客年龄排序泰坦尼克数据。

titanic.sort_values(by="Age").head()

执行后,表格行会按照Age列从低到高重新排列,输出结果将年龄最小的乘客排在最前面。

按多个列排序(含降序)

需求:按船舱等级与年龄排序,且均为降序。

titanic.sort_values(by=["Pclass", "Age"], ascending=False).head()

通过DataFrame.sort_values,表格行会按照定义的列(可为一个或多个)进行排序,且索引会跟随行序一起移动。ascending参数既可以传单个布尔值,也可以传与by等长的布尔列表,实现"部分列升序、部分列降序"的混合排序。

源码视角:sort_values 的参数细节

从 pandas/core/frame.py#L9195-L9206 的签名可以看到,DataFrame.sort_values支持以下关键参数:

参数默认值作用
by必填排序依据的列名或列名列表
axis0('index')沿行方向还是沿列方向排序
ascendingTrue是否升序;传列表可为每列单独指定
kind'quicksort'排序算法(quicksort / mergesort / heapsort / stable)
na_position'last'缺失值排在末尾('first'则排在最前)
ignore_indexFalseTrue时重排索引为 0..n-1
keyNone排序前对值应用的向量化 key 函数(类似内置sorted的 key,但要求接收并返回 Series)

其中mergesortstable是仅有的稳定排序算法,能保持相等键的原始相对顺序;key参数则允许你自定义排序规则(例如忽略大小写、按绝对值排序等)。更详细的排序讲解可参见 用户指南 sorting 章节 中关于排序的说明。

长表转宽表:pivot

我们先用空气质量数据构造一个小规模的演示子集:只保留no2数据,且每个站点(location)只取前两条测量记录。

# 只保留 no2 数据 no2 = air_quality[air_quality["parameter"] == "no2"] # 按时间排序后,对每个站点用 head(2) 取前两条记录 no2_subset = no2.sort_index().groupby(["location"]).head(2) no2_subset

此时no2_subset仍然是一个长表:同一站点、不同时刻的测量值分布在多行中。

需求:将三个站点的数值并排展示为独立列

no2_subset.pivot(columns="location", values="value")

DataFrame.pivot纯数据重排操作:它要求每个 index/column 组合只对应一个唯一的值,不做任何聚合。转换后,location的每个取值(BETR801、FR04014、London Westminster)成为新 DataFrame 的列,原有的 datetime 索引保持不变。

注意:当pivot不显式指定index参数时,会沿用现有的索引(行标签)。

长转宽的实际价值:多序列同时绘图

由于 pandas 天然支持对多列数据直接绘图(参见 绘图教程),长表转宽表后即可在同一个坐标系中绘制三个站点的 NO₂ 时间序列曲线:

no2.head() no2.pivot(columns="location", values="value").plot()

这一用法正是"长转宽"最典型的实战场景——把时间序列从"堆叠的长表"变成"并列的宽表"以便对比可视化。

源码视角:pivot 的纯重排语义

从 pandas/core/frame.py#L13926-L14075 的实现看,DataFrame.pivot的完整签名是pivot(*, columns, index=no_default, values=no_default)

  • columns:必填,用于构成新 DataFrame 的列;
  • index:可选,用于构成新 DataFrame 的索引,不传则使用现有索引;
  • values:可选,用于填充新 DataFrame 的数值列,不指定则使用其余所有列并生成层级化(MultiIndex)列。

该方法内部委托给 pandas/core/reshape/pivot.py 中的模块级pivot函数执行。当某个index/columns组合存在多个值时,会抛出ValueError: Index contains duplicate entries, cannot reshape——此时就需要改用支持聚合的pivot_table。源码 docstring 中给出了多列、多索引组合 pivot 的完整示例,例如df.pivot(index="foo", columns=["lev2", "lev3"], values="values")会生成两级层级列。

透视表:pivot_table(带聚合)

需求:汇总各站点 NO₂ 与 PM2.5 的平均浓度

当同一个 index/column 组合对应多个取值(本例中同一站点、同一参数有多个时刻的测量值)时,pivot无法处理,必须使用pivot_table并通过聚合函数合并这些值:

air_quality.pivot_table( values="value", index="location", columns="parameter", aggfunc="mean" )

输出结果中,行索引为三个站点,列(parameter)分为no2与 Particulate matter,单元格为各自浓度的均值。aggfunc="mean"表示对同一(location, parameter)组合下的多个测量值取平均。

添加行列小计(margins)

pivot_table 是电子表格软件中非常经典的概念。若需要同时输出行/列维度的小计(subtotals),将margins参数设为True

air_quality.pivot_table( values="value", index="location", columns="parameter", aggfunc="mean", margins=True, )

结果会在末尾追加一行和一列All,分别给出按列、按行聚合的均值。

源码视角:pivot_table 与 groupby 的等价关系

pivot_tableDataFrame.groupby存在直接的内在联系——同一结果完全可以通过按parameterlocation分组推导出来:

air_quality.groupby(["parameter", "location"])[["value"]].mean()

从 pandas/core/frame.py#L14077-L14090 的签名可见,DataFrame.pivot_table支持远比教程示例更丰富的参数:

参数默认值作用
valuesNone要聚合的列(可多个)
index/columnsNone分组键,可传列名、Grouper、数组或它们的序列
aggfunc"mean"聚合函数;可传函数、函数列表或 dict(dict 时 key 为列名)
fill_valueNone聚合后用于替换缺失值的标量
marginsFalse是否添加All行列小计
margins_name"All"小计行列的名称
dropnaTrue是否排除全为 NaN 的列;同时影响 margins 的计算
observedTrue当分组键为 Categorical 时,是否只显示实际出现的类别
sortTrue结果是否排序

aggfunc传入函数列表时,结果列会形成以函数名(如meansum)为顶层、原列名为次层的层级列结构;当传入 dict 时,可以为不同列指定不同聚合函数。fill_value则是在聚合完成后统一回填缺失值(如 0)。更完整的说明见 用户指南 pivot tables 章节。

宽表转长表:melt

从上一节生成的宽表出发,先用reset_index为 DataFrame 添加一个新索引:

no2_pivoted = no2.pivot(columns="location", values="value").reset_index() no2_pivoted.head()

此时三个站点的测量值分布在三列(BETR801、FR04014、London Westminster)中,date.utc作为普通列保留,这是一个典型的宽表。

需求:将所有 NO₂ 测量值收集到单列(长格式)

no_2 = no2_pivoted.melt(id_vars="date.utc") no_2.head()

DataFrame.melt将宽表转换为长表:所有未在id_vars中列出的列会被"融化"合并到两列中——一列存放原列头名称(默认名为variable),一列存放对应的数值(默认名为value)。

更精细地控制 melt 的参数

melt的参数可以显式定义,以获得更可读的输出:

no_2 = no2_pivoted.melt( id_vars="date.utc", value_vars=["BETR801", "FR04014", "London Westminster"], value_name="NO_2", var_name="id_location", ) no_2.head()

各参数的作用如下:

  • value_vars:指定要"融化"合并的列(不指定则默认融化除id_vars外的所有列);
  • value_name:自定义数值列的列名,替代默认的value
  • var_name:自定义存放原列头名称的列名,缺省时取索引名或默认的variable

简言之,value_namevar_name只是两个生成列的命名参数,真正决定"融化哪些列"的是id_varsvalue_vars

源码视角:melt 的完整参数与实现

从 pandas/core/frame.py#L14729-L14867 与 pandas/core/reshape/melt.py#L44-L53 可以看到,melt还额外支持两个参数:

  • col_level:当列是 MultiIndex 时,指定熔化的层级;
  • ignore_index(默认True):为False时保留原始索引(索引标签会按需重复)。

此外,melt对应的模块级函数pd.melt(frame, ...)DataFrame.melt行为完全一致。melt 的实现会先通过ensure_list_varsid_vars/value_vars规范化为列表,再将各列数据对齐堆叠。当列是 MultiIndex 且参数未以元组列表形式给出时会抛出ValueError

melt 在实战中的典型应用包括:宽表数据入库前转换为长表、多个测量列合并为"指标 + 数值"两列、以及为绘图库(如 seaborn)准备"tidy"输入。宽转长的更多细节见 用户指南 melt 章节。

速记要点

  • 按一列或多列排序使用sort_values
  • pivot只是数据的纯重排(long → wide),pivot_table额外支持聚合
  • pivot的逆操作是melt(wide → long),二者互为反向;
  • 遇到"同一 index/column 组合有多个值"时,pivot会抛ValueError,应改用pivot_table
  • pivot_tablegroupby等价:air_quality.groupby(["parameter", "location"])[["value"]].mean()可复现同一结果。

完整的表格重塑与透视操作总览,参见 用户指南 reshaping and pivoting 章节,以及本教程所在系列 入门教程索引。

【免费下载链接】pandasFlexible and powerful data analysis / manipulation library for Python, providing labeled data structures similar to R data.frame objects, statistical functions, and much more项目地址: https://gitcode.com/gh_mirrors/pa/pandas

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询