使用Polars将含杂乱数据的字符串列转为数值(适配懒加载与大数据)
Polars 大样本生物标志物字符串列懒加载清理方案
需求说明
需处理含CRP、CRP2、CRP3等生物标志物列的数据集(最小示例仅含1列),样本量3500万行(实际场景11列、近4000万行),按以下规则清理字符串列:
- 不以
<或>开头的字符串:提取数值部分转为数值类型 - 空值(含
None、"NA"):保留,代表无测量值 <X格式:替换为该列中数值小于X的有效观测的中位数,对应区间无数据则设为空>X格式:替换为该列中数值大于X的有效观测的中位数,对应区间无数据则设为空
要求全程采用Polars懒加载(LazyFrame)模式,减少内存占用,适配大样本场景。
最小示例实现
基于给定的最小示例数据演示完整流程:
import polars as pl # 构建示例LazyFrame df_lazy = pl.LazyFrame({ "Current": ["<4", "3", "2", None, ">5", "10"], "Goal": [2.5, 3, 2, None, 10, 10] }) # 定义处理单列的函数 def clean_biomarker_col(col_name: str) -> pl.Expr: # 1. 统一处理空值:将"NA"转为None col = pl.col(col_name).str.replace("NA", None) # 2. 提取前缀(<、>)和对应的数值X prefix = col.str.extract(r"^([<>])", 1) x_val = col.str.extract(r"^[<>]?(\d+\.?\d*)", 1).cast(pl.Float64) # 3. 提取列中的有效数值(非<、>开头的原始数值) valid_vals = col.filter(~col.str.starts_with_any(["<", ">"])).cast(pl.Float64) # 4. 计算各区间的中位数:小于X的中位数、大于X的中位数 # over(None)确保全局计算,适配懒加载的聚合逻辑 median_lt_x = valid_vals.filter(valid_vals < x_val).median().over(None) median_gt_x = valid_vals.filter(valid_vals > x_val).median().over(None) # 5. 根据前缀替换值,保留空值,普通数值直接用x_val return pl.when(col.is_null()) .then(col) .when(prefix == "<") .then(median_lt_x) .when(prefix == ">") .then(median_gt_x) .otherwise(x_val) .alias(col_name) # 应用处理函数到目标列 cleaned_df = df_lazy.with_columns( clean_biomarker_col("Current") ).collect() print(cleaned_df)
多列扩展(适配11列场景)
如果需要处理CRP、CRP2、CRP3等多列,只需批量应用处理函数:
# 定义需要处理的生物标志物列列表 biomarker_cols = ["CRP", "CRP2", "CRP3"] # 实际可扩展为11列 # 批量应用清理函数 cleaned_lazy_df = df_lazy.with_columns( [clean_biomarker_col(col) for col in biomarker_cols] ) # 大样本建议直接写入磁盘避免内存溢出 # cleaned_lazy_df.write_parquet("cleaned_biomarkers.parquet")
关键优化说明
- 全程采用LazyFrame:Polars会自动优化查询计划,避免全量数据加载到内存,适配千万级大样本
- 聚合计算用
over(None):确保在懒加载模式下正确计算全局中位数,而非分组统计 - 正则提取逻辑:兼顾整数、浮点数格式的数值,适配不同的生物标志物测量值格式
内容的提问来源于stack exchange,提问作者Vartholome
相关产品推荐
相关产品推荐

