如何在R-Polars中展开结构体列且不丢弃空结构体
问题描述
在R-Polars中处理DataFrame时,按逗号拆分列后展开结构体,若某列全为null,对应的展开列会丢失。比如示例中需要保留mpg、wt_0、wt_1和carb_0列,但当前代码执行后carb_0列缺失。
原代码(R)及问题输出
library(polars) mtcars$carb <- NA_real_ dt <- as_polars_df(mtcars) dt$select( pl$col("mpg"), wt = pl$col("wt")$cast(pl$String)$str$replace("\\.", ","), carb = pl$col("carb")$cast(pl$String)$str$replace("\\.", ",") )$with_columns( pl$col("wt")$str$split(",")$list$to_struct( fields = \(x) paste0("wt_", x), n_field_strategy = "max_width" ), pl$col("carb")$str$split(",")$list$to_struct( fields = \(x) paste0("carb_", x), n_field_strategy = "max_width" ) )$unnest()
输出:
shape: (32, 3) ┌──────┬──────┬──────┐ │ mpg ┆ wt_0 ┆ wt_1 │ │ --- ┆ --- ┆ --- │ │ f64 ┆ str ┆ str │ ╞══════╪══════╪══════╡ │ 21.0 ┆ 2 ┆ 62 │ │ 21.0 ┆ 2 ┆ 875 │ │ 22.8 ┆ 2 ┆ 32 │ │ 21.4 ┆ 3 ┆ 215 │ │ 18.7 ┆ 3 ┆ 44 │ │ … ┆ … ┆ … │ │ 30.4 ┆ 1 ┆ 513 │ │ 15.8 ┆ 3 ┆ 17 │ │ 19.7 ┆ 2 ┆ 77 │ │ 15.0 ┆ 3 ┆ 57 │ │ 21.4 ┆ 2 ┆ 78 │ └──────┴──────┴──────┘
Python版本的相同问题
正常情况(carb非空)
import polars as pl df = pl.read_csv(b"""mpg,wt,carb 1,1.2,2.3 2,3.4,4.5""") df.with_columns( pl.col("wt", "carb").cast(pl.String).str.replace(r"[.]", ","), ).with_columns( pl.col("wt").str.split(",") .list.to_struct("max_width", fields=lambda n: f"wt_{n}"), pl.col("carb").str.split(",") .list.to_struct("max_width", fields=lambda n: f"carb_{n}") ).unnest("wt", "carb")
输出:
shape: (2, 5) ┌─────┬──────┬──────┬────────┬────────┐ │ mpg ┆ wt_0 ┆ wt_1 ┆ carb_0 ┆ carb_1 │ │ --- ┆ --- ┆ --- ┆ --- ┆ --- │ │ i64 ┆ str ┆ str ┆ str ┆ str │ ╞═════╪══════╪══════╪════════╪════════╡ │ 1 ┆ 1 ┆ 2 ┆ 2 ┆ 3 │ │ 2 ┆ 3 ┆ 4 ┆ 4 ┆ 5 │ └─────┴──────┴──────┴────────┴────────┘
异常情况(carb全空)
df = pl.read_csv(b"""mpg,wt,carb 1,1.2, 2,3.4,""") df.with_columns( pl.col("wt", "carb").cast(pl.String).str.replace(r"[.]", ","), ).with_columns( pl.col("wt").str.split(",") .list.to_struct("max_width", fields=lambda n: f"wt_{n}"), pl.col("carb").str.split(",") .list.to_struct("max_width", fields=lambda n: f"carb_{n}") ).unnest("wt", "carb")
输出:
shape: (2, 3) ┌─────┬──────┬──────┐ │ mpg ┆ wt_0 ┆ wt_1 │ │ --- ┆ --- ┆ --- │ │ i64 ┆ str ┆ str │ ╞═════╪══════╪══════╡ │ 1 ┆ 1 ┆ 2 │ │ 2 ┆ 3 ┆ 4 │ └─────┴──────┴──────┘
解决方案
核心问题是:全为null的列经过str.split(",")后得到的是null列表,list.to_struct无法从空列表生成结构体字段。解决思路是将null替换为包含一个空元素的列表,确保结构体至少有一个字段。
R-Polars 修改后代码
library(polars) mtcars$carb <- NA_real_ dt <- as_polars_df(mtcars) dt$select( pl$col("mpg"), wt = pl$col("wt")$cast(pl$String)$str$replace("\\.", ","), carb = pl$col("carb")$cast(pl$String)$str$replace("\\.", ",") )$with_columns( # 处理wt列:正常拆分 pl$col("wt")$str$split(",")$list$to_struct( fields = \(x) paste0("wt_", x), n_field_strategy = "max_width" ), # 处理carb列:先将null替换为[""],再拆分生成结构体 pl$col("carb")$fill_null("")$str$split(",")$list$to_struct( fields = \(x) paste0("carb_", x), n_field_strategy = "max_width" ) )$unnest()
执行后会保留carb_0列,值全为""(空字符串),符合需求。
Python 版本修改后代码
import polars as pl df = pl.read_csv(b"""mpg,wt,carb 1,1.2, 2,3.4,""") df.with_columns( pl.col("wt", "carb").cast(pl.String).str.replace(r"[.]", ","), ).with_columns( pl.col("wt").str.split(",") .list.to_struct("max_width", fields=lambda n: f"wt_{n}"), # 填充null为空字符串后再拆分 pl.col("carb").fill_null("").str.split(",") .list.to_struct("max_width", fields=lambda n: f"carb_{n}") ).unnest("wt", "carb")
输出会包含carb_0列,值为空字符串。
补充说明
如果需要将空字符串再转回null,可以在unnest后添加一步:
# R版本 $with_columns(pl$col("carb_0")$str$to_null_if(""))
# Python版本 .with_columns(pl.col("carb_0").str.to_null_if(""))
内容的提问来源于stack exchange,提问作者user27247029
相关产品推荐
相关产品推荐

