
Polars unpivot 指南将宽表数据重塑为长表格式的完整实践【免费下载链接】polarsExtremely fast Query Engine for DataFrames, written in Rust项目地址: https://gitcode.com/GitHub_Trending/po/polars导读Polars 中unpivot逆透视也被称为 melt / pivot_longer是把一个 DataFrame 从宽表wide格式转换成整洁的长表long格式的核心数据重塑操作它把多列度量变量摊平为variable与value两列同时保留若干index列作为标识变量。本文以官方用户指南 unpivot.md 为主体结合 Python / Rust 双语言的 API 定义与底层引擎实现带你掌握on、index、variable_name、value_name各参数的完整语义与默认行为学会在 Eager 与 Lazy 两种执行模式下安全地完成宽表到长表的转换并理解其底层数据类型超类型合并、空输入兜底等实现细节。什么是 Unpivot用户指南对unpivot的定义非常精炼Unpivot unpivots a DataFrame from wide format to long format把 DataFrame 从宽表格式重塑为长表格式。在真实业务场景中宽表经常这样出现同一实体一行的多个指标被放在多列中例如温度、湿度、风速分列存储或是某一时段、某一方案的多个观测值并排陈列。这种形态便于阅读却不便于分组聚合、绘图或写入 SQL 友好存储。Unpivot 正是把这些横着排的度量列纵向堆叠形成统计与分析友好的 tidy data整洁数据结构。它与本仓库文档中另一篇 Pivot透视指南 描述的pivot操作互为反向变换pivot将某一列中的取值转置为新的列头x 轴并对原数值做 first / last / sum / min / max / mean / median / len 等聚合unpivot则把若干列降维为variable原列名与value原单元格值两列行数随之成倍增加。两者都围绕行列轴的重排展开共同构成数据重塑工具箱完整清单见 Transformations 索引页。准备数据集一个典型的宽表无论使用哪种语言示例都从同一个宽表出发。原文档在 unpivot.py 中通过 Python 构造了 4 列 3 行的数据import polars as pl df pl.DataFrame( { A: [a, b, a], B: [1, 3, 5], C: [10, 11, 12], D: [2, 4, 6], } ) print(df)输出如下shape: (3, 4) ┌─────┬─────┬─────┬─────┐ │ A ┆ B ┆ C ┆ D │ │ --- ┆ --- ┆ --- ┆ --- │ │ str ┆ i64 ┆ i64 ┆ i64 │ ╞═════╪═════╪═════╪═════╡ │ a ┆ 1 ┆ 10 ┆ 2 │ │ b ┆ 3 ┆ 11 ┆ 4 │ │ a ┆ 5 ┆ 12 ┆ 6 │ └─────┴─────┴─────┴─────┘这个示例结构小巧却完整地体现了宽表的典型特征A、B是描述观测对象身份的列标识变量而C、D是同一行上需要被堆叠的多个度量值变量。Rust 侧对应的示例见 docs/source/src/rust/user-guide/transformations/unpivot.rs使用df!宏构造完全一致的数据。核心操作执行一次 UnpivotPython 调用方式原文档给出的最小调用形式是out df.unpivot([C, D], index[A, B]) print(out)即把C、D两列作为待摊平的值变量value variables把A、B两列保留下来作为行标识identifier variables。执行后shape: (6, 4) ┌─────┬─────┬──────────┬───────┐ │ A ┆ B ┆ variable ┆ value │ │ --- ┆ --- ┆ --- ┆ --- │ │ str ┆ i64 ┆ str ┆ i64 │ ╞═════╪═════╪══════════╪═══════╡ │ a ┆ 1 ┆ C ┆ 10 │ │ b ┆ 3 ┆ C ┆ 11 │ │ a ┆ 5 ┆ C ┆ 12 │ │ a ┆ 1 ┆ D ┆ 2 │ │ b ┆ 3 ┆ D ┆ 4 │ │ a ┆ 5 ┆ D ┆ 6 │ └─────┴─────┴──────────┴───────┘可以看到原本 3 行中的每一行如今为每个被摊平的度量列C与D各产生一行共 6 行A、B作为标识列随行复制列名被记录到variable单元格数值被收集到value。注意文档与实现均明确结果的行顺序不作保证The resulting row order is unspecified因此上表仅为一种合法输出形态。若下游对行序敏感请自行追加sort等显式排序。Rust 调用方式在 Rust 中使用polarscrateuse polars::prelude::*;对应的调用为let out df.unpivot(Some([A, B]), [C, D])?;这里值得注意参数顺序与 Python 的差异Rust APIdf.unpivot(on, index)中第一个参数on是标识列第二个参数是待摊平的值列而 Python 的df.unpivot(on, index...)恰好相反第一个参数on是值变量、index才是标识列。None表示不保留标识列 / 全部用于摊平的语义需要对照文档使用例如 Rust 端传Some([A,B])是显式声明标识列。两套 API 的文档化输出示例见 unpivot.rs 与 crates/polars-ops/src/frame/unpivot.rs 中的 doctest。Eager 与 Lazy同一套 API原文档特别强调Eagerandlazyhave the same APIEager 与 Lazy 使用完全相同的 API。在 Python 端这一承诺体现在两个层面的同名方法上DataFrame.unpivot见 py-polars/src/polars/dataframe/frame.pyLazyFrame.unpivot见 py-polars/src/polars/lazyframe/frame.py。因此可以写出这样的惰性查询链out ( df.lazy() .unpivot([C, D], index[A, B]) .with_columns(pl.col(value).cast(pl.Float64)) .collect() ) print(out)在 Rust 端LazyFrame 上对应的节点由 crates/polars-lazy/src/frame/mod.rs 中的LazyFrame::unpivot(args: UnpivotArgsDSL)提供它把用户的on、index、variable_name、value_name参数打包后追加到逻辑计划中再交由优化器与物理引擎执行。由于 unpivot 的输出 schema列数、列名在给定参数后是完全可静态推导的与 pivot 不同pivot 的输出列由数据取值决定需预先声明on_columns详见 pivot.md所以它可以在 LazyFrame 上放心地参与惰性求值管线不会牺牲查询优化。对应 schema 推导函数位于 crates/polars-plan/src/plans/functions/schema.rs。参数详解on / index / variable_name / value_namePython 端DataFrame.unpivot的完整签名来自 frame.py 的 docstringdef unpivot( self, on: ColumnNameOrSelector | Sequence[ColumnNameOrSelector] | None None, *, index: ColumnNameOrSelector | Sequence[ColumnNameOrSelector] | None None, variable_name: str | None None, value_name: str | None None, ) - DataFrame:各参数语义如下参数类型默认值含义on列名 / 选择器cs.*/ 列表或NoneNone作为值变量measured variables参与摊平的列。若置空列表则没有任何列被摊平若为None默认则自动取所有不在index中的列index列名 / 选择器 / 列表或NoneNone作为**标识变量identifier variables**保留的列会在结果中按行复制variable_namestrvariable记录原列名的输出列名value_namestrvalue记录原单元格数值的输出列名几个关键行为需要特别注意on与index的互补性设置onNone时实现会自动以除index外的全部列作为值变量。反过来如果把某列同时交给两者会如何实践上应避免重叠指定让每个列在语义上只承担一种角色。重命名输出列当默认的variable/value与业务语义不符时用variable_name/value_name自定义例如df.unpivot([C, D], index[A, B], variable_namemetric, value_nameamount)。支持选择器selectorson与index都支持列选择器便于按 dtype 批量挑选例如官方 docstring 中给出的按数值类型摊平并保留字符串索引的写法import polars.selectors as cs df.unpivot(cs.numeric(), indexa)与 pandas 的对应关系docstring 明确说明如果你来自 pandas 生态此操作等价于pandas.DataFrame.melt但参数命名不同——Polars 的index对应 pandas 的id_varsPolars 的on对应 pandas 的value_vars在其他框架中它也可能被称作pivot_longer。这是理解本 API 最快的心智模型。源码视角Unpivot 在引擎中是如何实现的为了让文章既可操作也可溯源下面深入 crates/polars-ops/src/frame/unpivot.rs 看看真实实现路径。该文件定义了UnpivotDFtrait核心入口是unpivot与更通用的unpivot2pub trait UnpivotDF: IntoDf { fn unpivotI, J(self, on: OptionI, index: J) - PolarsResultDataFrame { ... } fn unpivot2(self, args: UnpivotArgsIR) - PolarsResultDataFrame { ... } }unpivot2接受统一的UnpivotArgsIR参数结构其字段恰好对应前文表格中的四个概念on、index、variable_name、value_name说明 Python 与 Rust 的高层 API 最终都会归一化到同一 IR 之上。实现中有几个值得注意的工程细节空结果的 schema 兜底当on为空或输入表宽度为 0 时代码仍会返回一个schema 正确但无数据的 DataFrame——variable列以空 String 列、value列以空 Null 列创建见unpivot.rs中Column::new_empty(variable_name, DataType::String)与new_empty(value_name, DataType::Null)。这意味着 unpivot 的结果 schema 在任何输入下都可预期这也是它能无缝用于 LazyFrame 静态 schema 推导的根基。多列摊平为单列前的类型合并由于不同值列的 dtype 可能不一致例如一列 Int64、一列 Float64将它们全部堆入同一value列前必须确定一个共同超类型supertype。实现中通过merge_dtypes_many(dtypes.iter())求出所有on列的公共类型后统一转换这正是长表能安全装下异构度量的关键一步。该文件开头的 doctest 也给出了A/Bstr/i32标识 C/Di32摊平前后的完整输入输出对照。性能取向value部分使用 Arrow 的concatenate_unchecked等手段做内存级拼接整体是列式友好的向量化路径而非逐行迭代。从引擎分层看调用链可概括为PythonDataFrame.unpivot/LazyFrame.unpivot→ Rust ops 层UnpivotDF::unpivot/unpivot2Eager或 plan 层节点Lazy→ schema 推导unpivot_schema→ 物理执行。Python 端类型标注同时维护在 py-polars/src/polars/_plr.pyi 的 stub 文件中IDE 用户可直接查看。应用场景与后续衔接unpivot 的典型应用包括回归到整洁数据tidy data把 Excel 式多指标并排的报表还原为id metric value的三列长表便于后续group_by聚合、filter筛选或转宽图表与统计建模的数据准备pivot_longer风格的长表是 seaborn / ggplot 等绘图库以及很多建模框架的默认输入格式作为 pivot 的可逆操作与 pivot 配合实现宽 ↔ 长的双向重塑满足不同分析阶段的列布局需求。一个自然衔接的后续步骤是摊平后配合pl.col(value).cast(...)统一类型、再用group_by(variable)做按指标分组统计。由于value列已完成超类型合并cast与分组都不会遇到列间类型冲突。参考资料本文主体来源docs/source/user-guide/transformations/unpivot.mdPython 可运行示例docs/source/src/python/user-guide/transformations/unpivot.pyRust 可运行示例docs/source/src/rust/user-guide/transformations/unpivot.rsPythonDataFrame.unpivot完整签名与 docstringpy-polars/src/polars/dataframe/frame.pyRust 引擎实现crates/polars-ops/src/frame/unpivot.rsLazy 节点入口crates/polars-lazy/src/frame/mod.rs 与 schema 推导crates/polars-plan/src/plans/functions/schema.rs逆操作 pivot 指南docs/source/user-guide/transformations/pivot.md【免费下载链接】polarsExtremely fast Query Engine for DataFrames, written in Rust项目地址: https://gitcode.com/GitHub_Trending/po/polars创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考