在Polars中读取CSV后,如何过滤含空元素列表的行?
Polars解析CSV列表字符串后过滤含空元素行的解决办法
问题
用Polars解析CSV里的列表字符串(比如"[1.0, 0.0, -2.3]")时,空列表"[]"会被解析成[null],尝试用list.contains(None)过滤这类行时,会报错InvalidOperationError: 'is_in' operation not supported for dtype 'list[f64]',需要保留所有列表都不含空元素的行。
解决步骤
1. 优化列表解析逻辑
先把空列表字符串处理成空列表而非[null],避免后续过滤的麻烦:
import polars as pl data = b''' list1,list2,list3 "[]","[0.0, 1.2]","[3.4]" "[102, 506]","[12, 5.2]","[2.3]" "[15]","[0.5, 8.2]","[]" '''.strip() df = ( pl.scan_csv(data) .with_columns( pl.col("^list.*$") .str.strip_chars("[ ]") .str.split(",") # 处理空列表:分割后是空字符串列表就转成空列表,否则转float列表 .map_elements( lambda x: [] if x == [""] else [float(i) for i in x], return_dtype=pl.List(pl.Float64) ) ) .collect() )
2. 过滤目标行
现在可以直接通过检查列表长度来过滤(空列表长度为0,对应原数据的"[]"):
filtered_df = df.filter( pl.all_horizontal( pl.col("^list.*$").list.len() > 0 ) )
如果你的数据里还有其他含空元素的列表(比如"[1.0, , 3.0]"解析后是[1.0, null, 3.0]),可以用下面的方法检查列表中是否有null:
filtered_df = df.filter( pl.all_horizontal( ~pl.col("^list.*$").list.eval(pl.element().is_null()).any() ) )
一步到位的写法
把解析和过滤合并成一个链式调用:
filtered_df = ( pl.scan_csv(data) .with_columns( pl.col("^list.*$") .str.strip_chars("[ ]") .str.split(",") .map_elements(lambda x: [] if x == [""] else [float(i) for i in x], return_dtype=pl.List(pl.Float64)) ) .filter(pl.all_horizontal(pl.col("^list.*$").list.len() > 0)) .collect() )
运行后得到的结果就是期望的第二行:
shape: (1, 3) ┌────────────────┬────────────────┬───────────┐ │ list1 ┆ list2 ┆ list3 │ │ --- ┆ --- ┆ --- │ │ list[f64] ┆ list[f64] ┆ list[f64] │ ╞════════════════╪════════════════╪═══════════╡ │ [102.0, 506.0] ┆ [12.0, 5.2] ┆ [2.3] │ └────────────────┴────────────────┴───────────┘
原代码报错原因
list.contains(None)底层依赖is_in操作,但Polars目前不支持对list[f64]类型执行is_in,所以得换用list.eval检查元素是否为null,或者通过列表长度判断空列表。
内容的提问来源于stack exchange,提问作者Nemoos
相关产品推荐
相关产品推荐

