Skill Tracer
Skills › Scientific Agent Skills › polars-bio › file_io.md

file_io.md

210 lines · skills/polars-bio/references/file_io.md at 5f2bc03

Open on GitHub
1# File I/O (polars-bio 0.36.0)
2
3Use the [reader API](https://biodatageeks.org/polars-bio/api/reading/),
4[format guide](https://biodatageeks.org/polars-bio/features/reading/), and
5[writer API](https://biodatageeks.org/polars-bio/api/writing/).
6The examples with filenames are templates. Small synthetic local reads and
7round trips were exercised on 0.36.0/Polars 1.44.2; inspect real input schemas.
8

File I/O (polars-bio 0.36.0)

Use the reader API, format guide, and writer API. The examples with filenames are templates. Small synthetic local reads and round trips were exercised on 0.36.0/Polars 1.44.2; inspect real input schemas.

Raw lines 1 to 8
9## Select a format-specific entry point
11| Format | Eager / lazy | SQL registration | Native output |
12|---|---|---|---|
13| BED | `read_bed` / `scan_bed` | `register_bed` | No BED writer |
14| VCF text | `read_vcf` / `scan_vcf` | `register_vcf` | `write_vcf` / `sink_vcf` |
15| BCF binary | `read_bcf` / `scan_bcf` | `register_bcf` | No BCF writer |
16| VCF Zarr | `read_vcf_zarr` / `scan_vcf_zarr` | `register_vcf_zarr` | None |
17| BAM | `read_bam` / `scan_bam` | `register_bam` | `write_bam` / `sink_bam` |
18| SAM | `read_sam` / `scan_sam` | `register_sam` | `write_sam` / `sink_sam` |
19| CRAM | `read_cram` / `scan_cram` | `register_cram` (reference caveat below) | `write_cram` / `sink_cram` |
20| GFF / GTF | `read_gff` / `scan_gff`; `read_gtf` / `scan_gtf` | `register_gff` / `register_gtf` | None |
21| FASTA / FASTQ | `read_fasta` / `scan_fasta`; `read_fastq` / `scan_fastq` | `register_fasta` / `register_fastq` | Corresponding `write_*` / `sink_*` |
22| Hi-C pairs | `read_pairs` / `scan_pairs` | `register_pairs` | None |
23| Tabular text | `read_table` / `scan_table` | Use `from_polars` | Use Polars |
250.36.0 also has BGEN/PGEN, BigWig/BigBed, Cooler, MSA and structure readers.
26Consult their specific signatures and companion-file requirements when needed;
27this reference's examples concern genomic intervals and the formats above.

Select a format-specific entry point

FormatEager / lazySQL registrationNative output
BEDread_bed / scan_bedregister_bedNo BED writer
VCF textread_vcf / scan_vcfregister_vcfwrite_vcf / sink_vcf
BCF binaryread_bcf / scan_bcfregister_bcfNo BCF writer
VCF Zarrread_vcf_zarr / scan_vcf_zarrregister_vcf_zarrNone
BAMread_bam / scan_bamregister_bamwrite_bam / sink_bam
SAMread_sam / scan_samregister_samwrite_sam / sink_sam
CRAMread_cram / scan_cramregister_cram (reference caveat below)write_cram / sink_cram
GFF / GTFread_gff / scan_gff; read_gtf / scan_gtfregister_gff / register_gtfNone
FASTA / FASTQread_fasta / scan_fasta; read_fastq / scan_fastqregister_fasta / register_fastqCorresponding write_* / sink_*
Hi-C pairsread_pairs / scan_pairsregister_pairsNone
Tabular textread_table / scan_tableUse from_polarsUse Polars

0.36.0 also has BGEN/PGEN, BigWig/BigBed, Cooler, MSA and structure readers. Consult their specific signatures and companion-file requirements when needed; this reference's examples concern genomic intervals and the formats above.

Raw lines 9 to 28
29## BED and custom tables
31`read_bed`/`scan_bed` expose `chrom` (String), `start`/`end` (UInt32) and
32`name` (String). BED3 yields a null name. Extra BED6/BED12 fields are discarded,
33not auto-detected into extra columns. Do not lose strand or exon block structure
34by assuming a BED12 input is twelve output columns.
36```python
37import polars as pl
38import polars_bio as pb
40# Preserve native half-open coordinates.
41regions = pb.scan_bed("regions.bed", use_zero_based=True)
43# Preserve BED6 annotations; generic table readers do not convert coordinates.
44bed6 = pb.scan_table("regions.bed6", schema="bed6")
45bed6.config_meta.set(coordinate_system_zero_based=True)
47# Nonstandard, headerless TSV: explicit Polars schema.
48custom = pl.scan_csv("regions.tsv", separator="\t", has_header=False,
49 schema={"chrom": pl.String, "start": pl.Int64,
50 "end": pl.Int64, "label": pl.String})
51custom.config_meta.set(coordinate_system_zero_based=True)
52```
54Despite its `Dict` annotation, `read_table`/`scan_table` expect a named schema
55such as `"bed3"`/`"bed6"`/`"bed12"`, not a dictionary of Python types. They wrap
56Polars TSV scanning; pass Polars CSV options as appropriate. Handle `track`,
57`browser` and comment lines explicitly and validate field counts. BED12 block
58columns need parsing/explosion if analysis is at exon level, not outer span.
60There is no `pb.write_bed`: select the intended ordered BED columns and use
61Polars tab-separated output with no header after confirming 0-based coordinates.

BED and custom tables

read_bed/scan_bed expose chrom (String), start/end (UInt32) and name (String). BED3 yields a null name. Extra BED6/BED12 fields are discarded, not auto-detected into extra columns. Do not lose strand or exon block structure by assuming a BED12 input is twelve output columns.

import polars as pl
import polars_bio as pb

# Preserve native half-open coordinates.
regions = pb.scan_bed("regions.bed", use_zero_based=True)

# Preserve BED6 annotations; generic table readers do not convert coordinates.
bed6 = pb.scan_table("regions.bed6", schema="bed6")
bed6.config_meta.set(coordinate_system_zero_based=True)

# Nonstandard, headerless TSV: explicit Polars schema.
custom = pl.scan_csv("regions.tsv", separator="\t", has_header=False,
                     schema={"chrom": pl.String, "start": pl.Int64,
                             "end": pl.Int64, "label": pl.String})
custom.config_meta.set(coordinate_system_zero_based=True)

Despite its Dict annotation, read_table/scan_table expect a named schema such as "bed3"/"bed6"/"bed12", not a dictionary of Python types. They wrap Polars TSV scanning; pass Polars CSV options as appropriate. Handle track, browser and comment lines explicitly and validate field counts. BED12 block columns need parsing/explosion if analysis is at exon level, not outer span.

There is no pb.write_bed: select the intended ordered BED columns and use Polars tab-separated output with no header after confirming 0-based coordinates.

Raw lines 29 to 62
63## VCF, BCF and VCF Zarr
65```python
66variants = pb.scan_vcf("cohort.vcf.gz", use_zero_based=True,
67 info_fields=["AF"], format_fields=["GT", "DP"])
68binary = pb.scan_bcf("cohort.bcf", use_zero_based=True,
69 info_fields=[], format_fields=["GT"])
70local_zarr = pb.scan_vcf_zarr("cohort.vcz", use_zero_based=True,
71 info_fields=[], format_fields=[])
72```
74VCF and BCF entry points are distinct; passing `.bcf` to `read_vcf` is rejected.
75Base VCF columns include `chrom`, `start`, `end`, `id`, `ref`, `alt`, `qual` and
76`filter`. Executed VCF output had UInt32 bounds and Float64 `qual`. There is no
77universal raw `info` string: `info_fields=None` expands header declarations;
78`[]` excludes them. FORMAT behaves analogously. Inspect list versus scalar
79fields from their VCF Number declarations before filtering.
81Single-sample FORMAT appears at the top level, with collision handling such as
82`fmt_DP` when INFO already owns `DP`. Multisample `genotypes` is a struct of
83per-field lists; sample order is in `pb.get_metadata(df)["header"]["sample_names"]`.
84`samples=[...]` is case sensitive and follows requested order; missing names
85warn and are skipped. Validate the selected names/count instead of treating a
86warning as proof that every requested sample was loaded.
88BCF additionally supports `genotype_output="dosage"` with
89`format_fields=["GT"]`: nullable Int8 ALT counts, biallelic only, missing/partial
90calls null. It is not an imputed dosage-probability reader. This option is absent
91from text VCF. VCF Zarr uses local directory paths and has its own
92`genotype_encoding_raw` option; do not pass that option to VCF/BCF.
94VCF interval span is a representation of the record, not a complete model of
95structural-variant breakends or all allele effects. Confirm END, REF length,
96symbolic alleles and multiallelic normalization against the biological question.
97Variant-record counts are not allele counts or independent sample counts.

VCF, BCF and VCF Zarr

variants = pb.scan_vcf("cohort.vcf.gz", use_zero_based=True,
                       info_fields=["AF"], format_fields=["GT", "DP"])
binary = pb.scan_bcf("cohort.bcf", use_zero_based=True,
                     info_fields=[], format_fields=["GT"])
local_zarr = pb.scan_vcf_zarr("cohort.vcz", use_zero_based=True,
                             info_fields=[], format_fields=[])

VCF and BCF entry points are distinct; passing .bcf to read_vcf is rejected. Base VCF columns include chrom, start, end, id, ref, alt, qual and filter. Executed VCF output had UInt32 bounds and Float64 qual. There is no universal raw info string: info_fields=None expands header declarations; [] excludes them. FORMAT behaves analogously. Inspect list versus scalar fields from their VCF Number declarations before filtering.

Single-sample FORMAT appears at the top level, with collision handling such as fmt_DP when INFO already owns DP. Multisample genotypes is a struct of per-field lists; sample order is in pb.get_metadata(df)["header"]["sample_names"]. samples=[...] is case sensitive and follows requested order; missing names warn and are skipped. Validate the selected names/count instead of treating a warning as proof that every requested sample was loaded.

BCF additionally supports genotype_output="dosage" with format_fields=["GT"]: nullable Int8 ALT counts, biallelic only, missing/partial calls null. It is not an imputed dosage-probability reader. This option is absent from text VCF. VCF Zarr uses local directory paths and has its own genotype_encoding_raw option; do not pass that option to VCF/BCF.

VCF interval span is a representation of the record, not a complete model of structural-variant breakends or all allele effects. Confirm END, REF length, symbolic alleles and multiallelic normalization against the biological question. Variant-record counts are not allele counts or independent sample counts.

Raw lines 63 to 98
99### VCF writes and fidelity
101```python
102# Retains per-record INFO/FORMAT key layout as well as source metadata.
103lf = pb.scan_vcf("cohort.vcf", use_zero_based=True,
104 preserve_record_layout=True)
105pb.sink_vcf(lf.filter(pl.col("qual") > 30), "filtered.vcf.bgz")
106```
108Keep `_vcf_info_keys`, `_vcf_format_keys` and their metadata to retain source
109key layout. Numeric values can still be canonicalized, so this is not a byte
110identity guarantee. Local input header text is preserved; remote headers are
111rebuilt from typed metadata and can lose free-form provenance lines. Added
112annotation fields require correct header declarations. A declared `INFO_<id>`
113column can supply source INFO values when an annotation column shadows `<id>`.
114Write a small file and re-read with a VCF parser before relying on a transformed
115header/schema. BCF is input-only; `write_vcf` emits text even if given a misleading
116extension, so use `.vcf`, `.vcf.gz` or `.vcf.bgz`.

VCF writes and fidelity

# Retains per-record INFO/FORMAT key layout as well as source metadata.
lf = pb.scan_vcf("cohort.vcf", use_zero_based=True,
                  preserve_record_layout=True)
pb.sink_vcf(lf.filter(pl.col("qual") > 30), "filtered.vcf.bgz")

Keep _vcf_info_keys, _vcf_format_keys and their metadata to retain source key layout. Numeric values can still be canonicalized, so this is not a byte identity guarantee. Local input header text is preserved; remote headers are rebuilt from typed metadata and can lose free-form provenance lines. Added annotation fields require correct header declarations. A declared INFO_<id> column can supply source INFO values when an annotation column shadows <id>. Write a small file and re-read with a VCF parser before relying on a transformed header/schema. BCF is input-only; write_vcf emits text even if given a misleading extension, so use .vcf, .vcf.gz or .vcf.bgz.

Raw lines 99 to 117
118## BAM, SAM and CRAM
120Executed SAM/BAM schemas contain `name`, `chrom`, `start`, `end`, `flags`,
121`cigar`, `mapping_quality`, `mate_chrom`, `mate_start`, `sequence`,
122`quality_scores`, `template_length`. Bounds and flags are UInt32;
123`template_length` is Int32. Unmapped positions can be null. Read optional tags
124with `tag_fields=["NM", "MD"]`; `infer_tag_types=True` samples 100 records by
125default. For unsampled/custom tags supply SAM hints such as `"pt:i"` or
126`"ML:B:C"`, not a guessed string cast.
128```python
129alignments = pb.scan_bam("reads.bam", use_zero_based=True,
130 tag_fields=["NM", "MD"])
131cram = pb.scan_cram("reads.cram", reference_path="reference.fa",
132 use_zero_based=True)
133pb.sink_bam(alignments, "filtered.bam", sort_on_write=True)
134```
136An index is optional for a full sequential scan. Neighboring BAI/CSI (BAM),
137CRAI (CRAM), TBI/CSI (compressed VCF/GFF/GTF/pairs), or CSI (BCF) enables supported
138indexed paths. Validate a region query against a full-scan fixture before
139assuming a complex filter is pushed down. Keep index and data versions matched.
141External-reference CRAM reads require a local FASTA and `.fai`; embedded-reference
142files may omit it. `register_cram` and `depth` lack `reference_path`; use a reader
143plus `from_polars` for SQL, or convert external-reference CRAM to BAM for depth.
144CRAM MD/NM tags have an upstream reader limitation; use BAM when those tags are
145required. Verify contig names, lengths and reference identity, not just a filename.
147`write_*` returns the row count; `sink_*` returns None. BAM/SAM/CRAM offer
148`sort_on_write`; sorting may buffer data. CRAM writers **require** `reference_path`.
149Preserve sequence headers and coordinate metadata, verify reference ordering for
150index creation, and create/check an index separately; writing is not evidence of
151an index. Alignment-span overlap includes CIGAR-skipped regions unless the reads
152are split into aligned blocks; choose depth for CIGAR-aware coverage instead.

BAM, SAM and CRAM

Executed SAM/BAM schemas contain name, chrom, start, end, flags, cigar, mapping_quality, mate_chrom, mate_start, sequence, quality_scores, template_length. Bounds and flags are UInt32; template_length is Int32. Unmapped positions can be null. Read optional tags with tag_fields=["NM", "MD"]; infer_tag_types=True samples 100 records by default. For unsampled/custom tags supply SAM hints such as "pt:i" or "ML:B:C", not a guessed string cast.

alignments = pb.scan_bam("reads.bam", use_zero_based=True,
                         tag_fields=["NM", "MD"])
cram = pb.scan_cram("reads.cram", reference_path="reference.fa",
                    use_zero_based=True)
pb.sink_bam(alignments, "filtered.bam", sort_on_write=True)

An index is optional for a full sequential scan. Neighboring BAI/CSI (BAM), CRAI (CRAM), TBI/CSI (compressed VCF/GFF/GTF/pairs), or CSI (BCF) enables supported indexed paths. Validate a region query against a full-scan fixture before assuming a complex filter is pushed down. Keep index and data versions matched.

External-reference CRAM reads require a local FASTA and .fai; embedded-reference files may omit it. register_cram and depth lack reference_path; use a reader plus from_polars for SQL, or convert external-reference CRAM to BAM for depth. CRAM MD/NM tags have an upstream reader limitation; use BAM when those tags are required. Verify contig names, lengths and reference identity, not just a filename.

write_* returns the row count; sink_* returns None. BAM/SAM/CRAM offer sort_on_write; sorting may buffer data. CRAM writers require reference_path. Preserve sequence headers and coordinate metadata, verify reference ordering for index creation, and create/check an index separately; writing is not evidence of an index. Alignment-span overlap includes CIGAR-skipped regions unless the reads are split into aligned blocks; choose depth for CIGAR-aware coverage instead.

Raw lines 118 to 153
154## Annotation, sequence and pairs schemas
156- GFF/GTF use UInt32 `start`/`end`, `type`, `source`, Float32 `score`, `strand`,
157 UInt32 `phase`. Default `attributes` is `List(Struct(tag, value))`. Use
158 `attr_fields=["ID", "Name"]` for those GFF3 keys or actual GTF keys such as
159 `["gene_id", "gene_name"]`; projected fields replace the attributes column.
160 Missing keys are not evidence that a gene lacks the biological annotation.
161- FASTA uses String `name`, `description`, `sequence`. FASTQ adds String
162 `quality_scores`. Validate sequence/quality lengths and quality encoding for
163 the producing technology. Both formats have writers and lazy sinks.
164- Pairs uses `readID`, `chrom1`, `pos1`, `chrom2`, `pos2`, `strand1`, `strand2`;
165 positions are UInt32 in the exercised fixture and obey `use_zero_based`.
166 These are two contact points, not automatically one contiguous interval.
168`describe_vcf`/`describe_bcf` inspect variant field definitions;
169`describe_bam`/`describe_sam`/`describe_cram` sample tags. Discovery from a sample
170cannot prove a rare tag never occurs. Check exact signatures: describe helpers
171do not all expose the same cloud or concurrency arguments.

Annotation, sequence and pairs schemas

  • GFF/GTF use UInt32 start/end, type, source, Float32 score, strand, UInt32 phase. Default attributes is List(Struct(tag, value)). Use attr_fields=["ID", "Name"] for those GFF3 keys or actual GTF keys such as ["gene_id", "gene_name"]; projected fields replace the attributes column. Missing keys are not evidence that a gene lacks the biological annotation.
  • FASTA uses String name, description, sequence. FASTQ adds String quality_scores. Validate sequence/quality lengths and quality encoding for the producing technology. Both formats have writers and lazy sinks.
  • Pairs uses readID, chrom1, pos1, chrom2, pos2, strand1, strand2; positions are UInt32 in the exercised fixture and obey use_zero_based. These are two contact points, not automatically one contiguous interval.

describe_vcf/describe_bcf inspect variant field definitions; describe_bam/describe_sam/describe_cram sample tags. Discovery from a sample cannot prove a rare tag never occurs. Check exact signatures: describe helpers do not all expose the same cloud or concurrency arguments.

Raw lines 154 to 172
173## Remote data and compression
175These are object/file reads, not a paginated REST service. For supported remote
176readers pass URI strings (`s3://`, `gs://`, `az://`, HTTP(S)) and individual
177options. SAM and VCF Zarr do not expose the common cloud options; do not assume
178cloud support for every `read_*`. A small public HTTPS BED read was executed;
179authenticated provider access remains untested.
181[Official cloud options](https://biodatageeks.org/polars-bio/features/cloud/):
183| Option | 0.36.0 behavior |
184|---|---|
185| `chunk_size` | MiB per ranged request, usually 8 for readers/scans; 64 for most registration helpers; `register_fasta` uses 8 |
186| `concurrent_fetches` | Default 8 on functions exposing it, not 1; supported backend/path dependent |
187| `allow_anonymous` | Default True for supported public S3/GCS reads; set False for authentication |
188| `max_retries`, `timeout` | Common defaults 5 and 300 seconds |
189| `enable_request_payer` | S3-only requester-pays option; enabling can incur charges |
191S3 supports `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`,
192`AWS_REGION`/`AWS_DEFAULT_REGION`, and custom `AWS_ENDPOINT_URL`. GCS supports
193`GOOGLE_APPLICATION_CREDENTIALS`. Azure uses `AZURE_STORAGE_ACCOUNT`,
194`AZURE_STORAGE_KEY`, and `AZURE_ENDPOINT_URL`; anonymous/concurrent capabilities
195are not equivalent to S3/GCS. Verify the provider's supported credential path
196instead of assuming every cloud SDK default credential chain is implemented.
197Never embed credentials in a published URL or source file.
199Object reads can require HEAD and ranged GET support. At eight 8-MiB fetches,
200up to 64 MiB can be in flight per stream, before parser and join allocations.
201`concurrent_fetches=1` reduces concurrency but does not guarantee GET-only signed
202URL compatibility for every HTTP/indexed path. Keep request limits small when
203probing, and do not interpret `limit()` as a guarantee of tiny network transfer.
205GZIP and BGZF differ: a `.gz` suffix does not prove BGZF or indexability. A plain
206GZIP BED fixture succeeded in 0.36.0 despite an outdated reader docstring.
207For VCF/FASTA/FASTQ writes, `.gz` selects GZIP and `.bgz` selects BGZF; native BAM
208is BGZF. Use BGZF plus the appropriate matching index for supported selective
209reads. Parallel decompression, fetching, and indexed parsing are separate
210features; none is guaranteed across every format merely by compressing a file.

Remote data and compression

These are object/file reads, not a paginated REST service. For supported remote readers pass URI strings (s3://, gs://, az://, HTTP(S)) and individual options. SAM and VCF Zarr do not expose the common cloud options; do not assume cloud support for every read_*. A small public HTTPS BED read was executed; authenticated provider access remains untested.

Official cloud options:

Option0.36.0 behavior
chunk_sizeMiB per ranged request, usually 8 for readers/scans; 64 for most registration helpers; register_fasta uses 8
concurrent_fetchesDefault 8 on functions exposing it, not 1; supported backend/path dependent
allow_anonymousDefault True for supported public S3/GCS reads; set False for authentication
max_retries, timeoutCommon defaults 5 and 300 seconds
enable_request_payerS3-only requester-pays option; enabling can incur charges

S3 supports AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION/AWS_DEFAULT_REGION, and custom AWS_ENDPOINT_URL. GCS supports GOOGLE_APPLICATION_CREDENTIALS. Azure uses AZURE_STORAGE_ACCOUNT, AZURE_STORAGE_KEY, and AZURE_ENDPOINT_URL; anonymous/concurrent capabilities are not equivalent to S3/GCS. Verify the provider's supported credential path instead of assuming every cloud SDK default credential chain is implemented. Never embed credentials in a published URL or source file.

Object reads can require HEAD and ranged GET support. At eight 8-MiB fetches, up to 64 MiB can be in flight per stream, before parser and join allocations. concurrent_fetches=1 reduces concurrency but does not guarantee GET-only signed URL compatibility for every HTTP/indexed path. Keep request limits small when probing, and do not interpret limit() as a guarantee of tiny network transfer.

GZIP and BGZF differ: a .gz suffix does not prove BGZF or indexability. A plain GZIP BED fixture succeeded in 0.36.0 despite an outdated reader docstring. For VCF/FASTA/FASTQ writes, .gz selects GZIP and .bgz selects BGZF; native BAM is BGZF. Use BGZF plus the appropriate matching index for supported selective reads. Parallel decompression, fetching, and indexed parsing are separate features; none is guaranteed across every format merely by compressing a file.

Raw lines 173 to 210