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 | 必填 | 排序依据的列名或列名列表 |
axis | 0('index') | 沿行方向还是沿列方向排序 |
ascending | True | 是否升序;传列表可为每列单独指定 |
kind | 'quicksort' | 排序算法(quicksort / mergesort / heapsort / stable) |
na_position | 'last' | 缺失值排在末尾('first'则排在最前) |
ignore_index | False | 为True时重排索引为 0..n-1 |
key | None | 排序前对值应用的向量化 key 函数(类似内置sorted的 key,但要求接收并返回 Series) |
其中mergesort与stable是仅有的稳定排序算法,能保持相等键的原始相对顺序;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_table与DataFrame.groupby存在直接的内在联系——同一结果完全可以通过按parameter与location分组推导出来:
air_quality.groupby(["parameter", "location"])[["value"]].mean()从 pandas/core/frame.py#L14077-L14090 的签名可见,DataFrame.pivot_table支持远比教程示例更丰富的参数:
| 参数 | 默认值 | 作用 |
|---|---|---|
values | None | 要聚合的列(可多个) |
index/columns | None | 分组键,可传列名、Grouper、数组或它们的序列 |
aggfunc | "mean" | 聚合函数;可传函数、函数列表或 dict(dict 时 key 为列名) |
fill_value | None | 聚合后用于替换缺失值的标量 |
margins | False | 是否添加All行列小计 |
margins_name | "All" | 小计行列的名称 |
dropna | True | 是否排除全为 NaN 的列;同时影响 margins 的计算 |
observed | True | 当分组键为 Categorical 时,是否只显示实际出现的类别 |
sort | True | 结果是否排序 |
当aggfunc传入函数列表时,结果列会形成以函数名(如mean、sum)为顶层、原列名为次层的层级列结构;当传入 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_name与var_name只是两个生成列的命名参数,真正决定"融化哪些列"的是id_vars与value_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_vars将id_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_table与groupby等价: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),仅供参考