如何高效从数千个CSV提取指定两列唯一值构建Polars DataFrame
3000+TSV文件提取两列唯一值高效实现方案
你原有的逐个文件对比合并的实现,存在大量重复的全量DataFrame对比、内存释放操作,3000个文件的循环开销极高,且浪费了chr、pos列预排序的特性,下面提供两种更高性能的实现思路:
方案1:利用预排序特性的Polars原生实现
因为所有文件的chr、pos列已经预先排序,单个文件执行unique后仍会保持有序状态,不需要全量合并后再做全局散列去重,仅需做相邻对比即可完成全局去重,效率提升非常明显:
import polars as pl import glob # 定义文件匹配规则 tsv_pattern = "*.biobank.tsv.gz" df_list = [] for fn in glob.glob(tsv_pattern): # 仅读取需要的两列,单文件去重后保持顺序 single_df = pl.read_csv( fn, separator='\t', schema_overrides={"#chrom": pl.String}, n_threads=8, columns=["#chrom", "pos"], new_columns=["chr", "pos"] ).unique(maintain_order=True) df_list.append(single_df) # 合并所有有序小表后按有序规则去重,仅需相邻对比,1600万行几乎秒出结果 global_unique_df = pl.concat(df_list).unique(maintain_order=True) # 可根据需要导出结果:global_unique_df.write_csv("final_unique_chr_pos.csv")
方案2:无中间文件的DuckDB优化实现
你提到的Polars+DuckDB方案可以进一步简化,省略中间导出Parquet的磁盘IO步骤,DuckDB支持直接读取批量压缩TSV文件,一步完成全局去重:
import duckdb # 直接批量扫描所有gz文件,向量化执行一步去重 duck_conn = duckdb.connect() global_unique_df = duck_conn.execute(""" SELECT DISTINCT "#chrom" AS chr, pos FROM read_csv( '*.biobank.tsv.gz', sep = '\t', columns = {'#chrom': 'VARCHAR', 'pos': 'BIGINT'}, auto_detect = FALSE ) """).pl()
方案选型参考
- 若后续需要继续用Polars处理结果数据,优先选方案1,全程保持Polars生态无额外转换开销
- 若追求极致的查询效率、不需要额外处理中间过程,优先选方案2,DuckDB的多文件批量扫描优化比手动遍历文件效率更高
内容的提问来源于stack exchange,提问作者darked89
相关产品推荐
相关产品推荐

