
本文介绍如何在 Polars 中对存储 JSON 字符串的列进行条件过滤,重点演示 json_path_match 和 json_decode + unnest 两种原生、高性能方法,精准匹配键存在性与值相等条件。
本文介绍如何在 polars 中对存储 json 字符串的列进行条件过滤,重点演示 `json_path_match` 和 `json_decode + unnest` 两种原生、高性能方法,精准匹配键存在性与值相等条件。
在实际数据处理中,常遇到将结构化 JSON 数据以字符串形式存于 DataFrame 列(如日志标签、元数据字段)的场景。直接对这类字符串列做逻辑判断需谨慎——既要确保 JSON 格式有效,又要高效提取并校验嵌套字段。Polars 提供了专为 JSON 字符串设计的原生表达式,无需转换为 Python 对象,全程在 Rust 引擎内执行,兼顾性能与简洁性。
✅ 推荐方案:json_path_match(轻量、高效、一步过滤)
当仅需判断某路径是否存在且值满足条件时,pl.col("col").str.json_path_match("$.key") 是最优选择。它返回匹配的首个值(若无匹配则为 null),支持链式布尔运算:
import polars as pl
df = pl.DataFrame({
"tags": [
'{"ref":"@1", "area": "livingroom", "type": "elec"}',
'{"ref":"@2", "area": "kitchen"}',
'{"ref":"@3", "type": "elec"}'
],
"name": ["a", "b", "c"],
})
# 筛选:同时满足 —— "area" 键存在(非 null)且 "type" 值等于 "elec"
result = df.filter(
pl.col("tags").str.json_path_match("$.area").is_not_null(),
pl.col("tags").str.json_path_match("$.type") == "elec"
)
print(result)输出:
shape: (1, 2)
┌────────────────────────────────────────────────────┬──────┐
│ tags ┆ name │
│ --- ┆ --- │
│ str ┆ str │
╞════════════════════════════════════════════════════╪══════╡
│ {"ref":"@1", "area": "livingroom", "type": "elec"} ┆ a │
└────────────────────────────────────────────────────┴──────┘⚠️ 注意事项:
- json_path_match 使用 JSONPath 语法(如 $.area),不支持复杂数组索引;
- 若 JSON 格式非法,该表达式会抛出 InvalidArgumentError,生产环境建议配合 pl.when(...).then(...).otherwise(pl.lit(None)) 做容错;
- 多条件组合推荐用多个 filter() 参数(逻辑与),而非 & 运算符——更易读且避免空列报错。
? 进阶方案:json_decode + unnest(灵活、可复用、支持复杂操作)
若需对 JSON 内多个字段做组合计算、分组、聚合或后续多次访问,应先解码为结构体(struct)列,再展开(unnest):
# 解码 JSON 字符串为 struct,然后展开为独立列
df_struct = (
df
.with_columns(pl.col("tags").str.json_decode().alias("tags"))
.unnest("tags")
)
print(df_struct)输出:
shape: (3, 4) ┌─────┬────────────┬──────┬──────┐ │ ref ┆ area ┆ type ┆ name │ │ --- ┆ --- ┆ --- ┆ --- │ │ str ┆ str ┆ str ┆ str │ ╞═════╪════════════╪══════╪══════╡ │ @1 ┆ livingroom ┆ elec ┆ a │ │ @2 ┆ kitchen ┆ null ┆ b │ │ @3 ┆ null ┆ elec ┆ c │ └─────┴────────────┴──────┴──────┘
此时即可用标准列操作进行任意过滤:
# 等价但更直观的写法(支持 is_in, contains, 正则等)
final = df_struct.filter(
pl.col("area").is_not_null() & (pl.col("type") == "elec")
)? 小技巧:json_decode() 默认推断 schema,如需强类型控制(例如将 "ref" 显式设为 pl.String),可传入 schema 参数:
pl.col("tags").str.json_decode(schema={"ref": pl.String, "area": pl.String, "type": pl.String})
总结
- 单次简单过滤 → 优先用 json_path_match:零内存拷贝、表达式链式清晰、性能极致;
- 需多字段交互或后续分析 → 选用 json_decode + unnest:一次解码,长期复用,语义明确;
- 避免 apply(lambda x: json.loads(x).get(...)):触发 Python GIL,严重拖慢大规模数据处理;
- 所有操作均支持流式(streaming)模式,适用于 GB 级 JSON 日志解析场景。
掌握这两种模式,即可从容应对 Polars 中绝大多数 JSON 字符串列的筛选与转换需求。


















