polars-bio 是做什么的?
概述
polars-bio 是一个高性能 Python 库,专为基因组区间操作和生物信息学文件 I/O 设计,构建在 Polars、Apache Arrow 和 Apache DataFusion 之上。它提供了熟悉的 DataFrame 中心 API,用于区间算术(重叠、最近邻、合并、覆盖度、互补、减法)以及常见生物信息学格式(BED、VCF、BAM、CRAM、GFF/GTF、FASTA、FASTQ)的读写。
核心价值主张:
- 比 bioframe 快 6-38 倍(在真实基因组基准测试中)
- 支持流式/核外处理大型基因组数据,通过 DataFusion 实现
- 云原生文件 I/O(S3、GCS、Azure),支持谓词下推
- 两种 API 风格:函数式(
pb.overlap(df1, df2))和方法链式(df1.lazy().pb.overlap(df2)) - SQL 接口,通过 DataFusion SQL 引擎查询基因组数据
何时使用此技能
当您需要执行以下操作时使用此技能:
- 执行基因组区间操作(重叠、最近邻、合并、覆盖度、互补、减法)
- 读写生物信息学文件格式(BED、VCF、BAM、CRAM、GFF/GTF、FASTA、FASTQ)
- 处理无法一次性加载到内存中的大型基因组数据集(流式模式)
- 对基因组数据文件运行 SQL 查询
- 从 bioframe 迁移到更快的替代方案
- 从 BAM/CRAM 文件计算读深度/堆积
- 处理包含基因组区间的 Polars DataFrame
快速开始
安装
需要 Python 3.11–3.14(参见 PyPI)。
uv pip install "polars-bio==0.31.0"
如需 pandas 兼容性(pandas ≥3.0):
uv pip install "polars-bio[pandas]==0.31.0"
基本重叠示例
import polars as pl
import polars_bio as pb
# 创建两个区间 DataFrame
df1 = pl.DataFrame({
"chrom": ["chr1", "chr1", "chr1"],
"start": [1, 5, 22],
"end": [6, 9, 30],
})
df2 = pl.DataFrame({
"chrom": ["chr1", "chr1"],
"start": [3, 25],
"end": [8, 28],
})
# 函数式 API(默认返回 LazyFrame)
result = pb.overlap(df1, df2)
result_df = result.collect()
# 直接获取 DataFrame
result_df = pb.overlap(df1, df2, output_type="polars.DataFrame")
# 方法链式 API(通过 LazyFrame 的 .pb 访问器)
result = df1.lazy().pb.overlap(df2)
result_df = result.collect()
读取 BED 文件
import polars_bio as pb
# 急切读取(加载整个文件)
df = pb.read_bed("regions.bed")
# 惰性扫描(流式,适用于大文件)
lf = pb.scan_bed("regions.bed")
result = lf.collect()
核心能力
1. 基因组区间操作
polars-bio 提供 8 种核心区间操作,用于基因组范围运算。所有操作均接受包含 chrom、start、end 列的 Polars DataFrame(列名可配置)。默认返回 LazyFrame(使用 output_type="polars.DataFrame" 可获取急切结果)。
操作:
overlap/count_overlaps- 查找或计数两组区间之间的重叠(overlap_output="left"自 0.30.0 起仅返回 df1 中的命中)nearest- 查找最近区间(可配置k、overlap、distance参数)merge- 合并同一组内的重叠/邻接区间cluster- 为重叠区间分配簇 IDcoverage- 计算每个区间的覆盖度计数(双输入操作)complement- 查找基因组中区间之间的间隙subtract- 移除与另一组重叠的区间部分
示例:
import polars_bio as pb
# 查找重叠区间(返回 LazyFrame)
result = pb.overlap(df1, df2, suffixes=("_1", "_2"))
# 计算每个区间的重叠计数
counts = pb.count_overlaps(df1, df2)
# 合并重叠区间
merged = pb.merge(df1)
# 查找最近区间
nearest = pb.nearest(df1, df2)
# 收集任意 LazyFrame 结果到 DataFrame
result_df = result.collect()
参考: 参见 references/interval_operations.md 了解所有操作、参数、输出模式及性能考虑的详细文档。
2. 生物信息学文件 I/O
使用 read_*、scan_*、write_* 和 sink_* 函数读写常见的生物信息学格式。支持云存储(S3、GCS、Azure)和压缩(GZIP、BGZF)。
支持的格式:
- BED - 基因组区间(
read_bed、scan_bed、write_*通过通用接口) - VCF - 遗传变异(
read_vcf、scan_vcf、write_vcf、sink_vcf) - VCF Zarr - 适合分析的 Zarr 存储(
read_vcf_zarr、scan_vcf_zarr;本地目录路径) - BAM - 比对读段(
read_bam、scan_bam、write_bam、sink_bam) - CRAM - 压缩比对(
read_cram、scan_cram、write_cram、sink_cram) - GFF - 基因注释(
read_gff、scan_gff) - GTF - 基因注释(
read_gtf、scan_gtf) - FASTA - 参考序列(
read_fasta、scan_fasta、write_fasta、sink_fasta) - FASTQ - 测序读段(
read_fastq、scan_fastq、write_fastq、sink_fastq) - SAM - 文本比对(
read_sam、scan_sam、write_sam、sink_sam) - Hi-C pairs - 染色质接触(
read_pairs、scan_pairs)
示例:
import polars_bio as pb
# 读取 VCF 文件
variants = pb.read_vcf("samples.vcf.gz")
# 惰性扫描 BAM 文件(流式)
alignments = pb.scan_bam("aligned.bam")
# 读取 GFF 注释
genes = pb.read_gff("annotations.gff3")
# 云存储(单个参数,非字典)
df = pb.read_bed("s3://bucket/regions.bed",
allow_anonymous=True)
参考: 参见 references/file_io.md 了解每种格式的列模式、参数、云存储选项及压缩支持。
3. SQL 数据处理
将生物信息学文件注册为表,并使用 DataFusion SQL 进行查询。结合 SQL 的强大功能和 polars-bio 的基因组感知读取器。
import polars as pl
import polars_bio as pb
# 将文件注册为 SQL 表(路径在前,name= 关键字)
pb.register_vcf("samples.vcf.gz", name="variants")
pb.register_bed("target_regions.bed", name="regions")
# 使用 SQL 查询(返回 LazyFrame)
result = pb.sql("SELECT chrom, start, end, ref, alt FROM variants WHERE qual > 30")
result_df = result.collect()
# 将 Polars DataFrame 注册为 SQL 表
pb.from_polars("my_intervals", df)
result = pb.sql("SELECT * FROM my_intervals WHERE chrom = 'chr1'").collect()
参考: 参见 references/sql_processing.md 了解注册函数、SQL 语法和示例。
4. 堆积操作
从 BAM/CRAM 文件计算每个碱基的读取深度,支持 CIGAR 感知的深度计算。
import polars_bio as pb
# 计算 BAM 文件的深度
depth_lf = pb.depth("aligned.bam")
depth_df = depth_lf.collect()
# 带质量过滤
depth_lf = pb.depth("aligned.bam", min_mapping_quality=20)
参考: 参见 references/pileup_operations.md 了解参数和集成模式。
关键概念
坐标系统
polars-bio 默认使用 1-based 坐标(基因组惯例)。可以通过全局设置更改:
import polars_bio as pb
# 切换到 0-based 半开区间坐标(默认为 1-based / False)
pb.set_option("datafusion.bio.coordinate_system_zero_based", True)
# 切换回 1-based(默认)
pb.set_option("datafusion.bio.coordinate_system_zero_based", False)
I/O 函数也接受 use_zero_based 参数,用于在生成的 DataFrame 上设置坐标元数据:
# 读取 BED 并显式设置 0-based 元数据
df = pb.read_bed("regions.bed", use_zero_based=True)
重要: BED 文件在文件格式中始终是 0-based 半开区间。polars-bio 在读取 BED 文件时自动处理转换。坐标元数据由 I/O 函数附加到 DataFrame,并在操作中传播。
两种 API 风格
函数式 API - 独立函数,显式输入:
result = pb.overlap(df1, df2, suffixes=("_1", "_2"))
merged = pb.merge(df)
方法链式 API - 通过 LazyFrame(非 DataFrame)的 .pb 访问器:
result = df1.lazy().pb.overlap(df2)
merged = df.lazy().pb.merge()
重要: 区间操作的 .pb 访问器仅适用于 LazyFrame。在 DataFrame 上,.pb 仅提供写操作(write_bam、write_vcf 等)。
方法链式支持流畅的管道:
# 链式区间操作(注意:overlap 输出带后缀的列,
# 所以在 merge 前需要重命名以期望 chrom/start/end)
result = (
df1.lazy()
.pb.overlap(df2)
.filter(pl.col("start_2") > 1000)
.select(
pl.col("chrom_1").alias("chrom"),
pl.col("start_1").alias("start"),
pl.col("end_1").alias("end"),
)
.pb.merge()
.collect()
)
探测-构建架构
对于双输入操作(overlap、nearest、count_overlaps、coverage),polars-bio 使用探测-构建连接策略:
- 第一个 DataFrame 是 探测方(被迭代)
- 第二个 DataFrame 是 构建方(建立索引用于查找)
为获得最佳性能,将较大的 DataFrame 作为第一个参数(探测方),较小的作为第二个(构建方)。
列约定
默认情况下,polars-bio 期望列名为 chrom、start、end。可以通过列表指定自定义列名:
result = pb.overlap(
df1, df2,
cols1=["chromosome", "begin", "finish"],
cols2=["chr", "pos_start", "pos_end"],
)
返回类型与收集结果
所有区间操作和 pb.sql() 默认返回 LazyFrame。使用 .collect() 物化结果,或传递 output_type="polars.DataFrame" 进行急切求值:
# 惰性(默认)- 需要时收集
result_lf = pb.overlap(df1, df2)
result_df = result_lf.collect()
# 急切 - 直接获取 DataFrame
result_df = pb.overlap(df1, df2, output_type="polars.DataFrame")
流式与核外处理
对于大于可用 RAM 的数据集,使用 scan_* 函数和流式执行:
# 惰性扫描文件
lf = pb.scan_bed("large_intervals.bed")
# 使用 Polars 流式处理(需要 polars ≥1.37,已随 polars-bio 捆绑安装)
result = lf.collect(engine="streaming")
DataFusion 流式默认在区间操作中启用,分批处理数据而不将完整数据集加载到内存中。
常见陷阱
-
.pb访问器在 DataFrame 与 LazyFrame 上的区别: 区间操作(overlap、merge 等)仅在LazyFrame.pb上可用。DataFrame.pb仅有写方法。在链式调用区间操作前,使用.lazy()进行转换。 -
LazyFrame 返回类型: 所有区间操作和
pb.sql()默认返回LazyFrame。不要忘记.collect()或使用output_type="polars.DataFrame"。 -
列名不匹配: polars-bio 默认期望
chrom、start、end列。如果列名不同,请使用cols1/cols2参数(以列表形式提供)。 -
坐标系统元数据: 区间操作从 I/O 函数或 DataFrame 的
config_meta读取坐标元数据。对于手动构建的 DataFrame,设置df.config_meta.set(coordinate_system_zero_based=True)(0-based)或False(1-based)。如果元数据缺失,polars-bio 会回退到全局datafusion.bio.coordinate_system_zero_based设置(并发出警告)。设置pb.set_option("datafusion.bio.coordinate_system_check", True)以引发MissingCoordinateSystemError异常。输入之间的系统不匹配会引发CoordinateSystemMismatchError。 -
探测-构建顺序很重要: 对于 overlap、nearest 和 coverage,第一个 DataFrame 作为探测方与第二个进行匹配。交换参数会改变哪些区间出现在左列与右列输出中,并可能影响性能。
-
INT32 位置限制: 基因组位置存储为 32 位整数,将坐标限制在约 21 亿。这足以应对所有已知基因组,但在自定义坐标空间中可能成为问题。
-
BAM 索引要求:
read_bam和scan_bam需要 BAM 文件旁边有.bai索引文件。如果缺少,请使用samtools index创建。 -
默认禁用并行执行: DataFusion 并行度默认为 1 个分区。对大型数据集启用:
pb.set_option("datafusion.execution.target_partitions", 8) -
CRAM 有单独的函数: 对于 CRAM 文件,使用
read_cram/scan_cram/register_cram(而不是read_bam)。CRAM 函数需要reference_path参数。
最佳实践
-
对大型文件使用
scan_*: 对于大于可用 RAM 的文件,优先使用scan_bed、scan_vcf等而不是read_*。扫描函数支持流式和谓词下推。 -
为大型数据集配置并行度:
import os pb.set_option("datafusion.execution.target_partitions", os.cpu_count()) -
使用 BGZF 压缩: BGZF 压缩文件(
.bed.gz、.vcf.gz)支持并行块解压,比普通 GZIP 快得多。 -
尽早选择列: 当只需要特定列时,尽早选择以减少内存使用:
df = pb.read_vcf("large.vcf.gz").select("chrom", "start", "end", "ref", "alt") -
直接使用云路径: 将 S3/GCS/Azure URI 直接传递给 read/scan/register 函数,而不是先下载文件。身份验证访问仅在访问这些云路径时使用云 SDK 凭据(
AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY、GOOGLE_APPLICATION_CREDENTIALS、Azure 默认值):df = pb.read_bed("s3://my-bucket/regions.bed", allow_anonymous=True) -
单次操作优先使用函数式 API,管道操作优先使用方法链式: 对于一次性操作使用
pb.overlap(),在构建多步管道时使用.lazy().pb.overlap()。
资源
references/
每个主要能力的详细文档:
-
interval_operations.md - 所有 8 种区间操作,包含参数、示例、输出模式和性能提示。基因组范围运算的核心参考。
-
file_io.md - 支持的格式表、每种格式的列模式、参数、云存储选项和压缩支持。所有生物信息学文件读写的参考。
-
sql_processing.md - 注册函数、SQL 语法参考和查询示例。内含 SQL 工作的指导。
-
pileup_operations.md -
depth函数的参数和集成模式。适用时可参考。