streampile
Forward-only pileups of coordinate-sorted SAM, BAM, and CRAM, and the alleles at every base.
Installation
pip install streampile
Quickstart
Building Pileups
A StreamingPileupBuilder reads records once and piles them up at the 0-based positions you ask for, moving forward only.
Give the AlignmentFile threads to decompress the BAM with, which pays at depth; for CRAM, also give it reference_filename.
>>> from pysam import AlignmentFile
>>> from streampile import StreamingPileupBuilder
>>>
>>> with (
... AlignmentFile("tests/data/reads.bam", threads=4) as reads,
... StreamingPileupBuilder(reads, min_base_quality=30) as builder,
... ):
... first = builder.pileup("chr1", 10)
... second = builder.pileup("chr1", 12)
>>>
>>> first.unfiltered_depth, first.filtered_depth, first.bases
(4, 4, ['A', 'T', 'G', 'A'])
>>> second.unfiltered_depth, second.filtered_depth, second.bases
(4, 3, ['G', 'G', 'G'])
At position 12, the base of lowqual is under the quality floor, so it counts toward unfiltered_depth only.
Filter reads with read_filter, and see each template once with templates(), which calls the bases of overlapping mates into one as fgbio's CallOverlappingConsensusBases does.
Overlapping mates read the same bases of their template twice: they agree where they read the same base, and disagree where they read different ones.
The mates of pair overlap from position 45 to 54, agree on T at position 50, and disagree at position 52, where the first reads G at Q40 and the second A at Q20:
position 40 45 50 55
pair 99 GATCGGATCCTAGGC
pair 147 GATCCTAAGCTAGCT
^ ^
By default, an agreeing base is called at the sum of the two qualities, and a disagreeing one as the base of the higher quality at their difference:
>>> with (
... AlignmentFile("tests/data/reads.bam") as reads,
... StreamingPileupBuilder(reads, read_filter=lambda read: read.is_paired) as builder,
... ):
... agreeing = builder.pileup("chr1", 50)
... disagreeing = builder.pileup("chr1", 52)
>>>
>>> for pileup in (agreeing, disagreeing):
... template = pileup.templates()[0]
... print(pileup.bases, pileup.qualities, template.base, template.qual)
['T', 'T'] [40, 40] T 80
['G', 'A'] [40, 20] G 20
Other strategies mask a disagreement, or keep the base of the higher quality as it is:
>>> from streampile import DisagreementStrategy
>>>
>>> for strategy in DisagreementStrategy:
... template = disagreeing.templates(disagreement=strategy)[0]
... print(strategy, template.base, template.qual)
consensus G 20
mask_both N 2
mask_lower_qual G 40
A mate whose base is under the quality floor does not vote, as it is left out of bases, so it neither masks nor lowers the other mate's base:
>>> with (
... AlignmentFile("tests/data/reads.bam") as reads,
... StreamingPileupBuilder(reads, min_base_quality=30) as builder,
... ):
... floored = builder.pileup("chr1", 52)
>>>
>>> template = floored.templates(disagreement=DisagreementStrategy.mask_both)[0]
>>> floored.bases, template.base, template.qual
(['G'], 'G', 40)
A template also holds its reads, its strand, and its distances to both of its ends:
>>> template = disagreeing.templates()[0]
>>> template.is_reverse, template.five_prime_distance, template.template_end_distance
(False, 12, 7)
Each entry also counts the template's bases from its base to the read's 5′ end and to the template's other end, the 5′ end of its mate in an FR pair, whose CIGAR comes from the MC tag:
>>> with (
... AlignmentFile("tests/data/reads.bam") as reads,
... StreamingPileupBuilder(reads) as builder,
... ):
... pileup = builder.pileup("chr1", 45)
>>>
>>> for entry in pileup.pileups:
... flag = entry.alignment.flag
... print(flag, entry.five_prime_distance, entry.template_end_distance)
0 5 None
99 5 14
147 14 5
The unpaired read has no template end, and the two mates of the pair mirror each other.
A read has one only when is_fr_pair calls its pair FR, as htsjdk 5.0.0 does: the forward read's aligned 5′ position is at or before the reverse read's.
Tapping Every Record
The function given as tap receives every record, in input order, once the builder has moved past it, so a record can be changed, e.g. tagged, while it is piled up.
Each entry's alignment is the very record read, so pass tap=writer.write to write the changed records to an AlignmentFile opened with template=reads.
Pileups see each read as it was when the builder read it; changes made after that, including in read_filter, reach tap and alignment but not later pileups.
Here every read is written to a new BAM, and a read whose base at position 10 is not the reference A is tagged XV with that base.
>>> from tempfile import mkdtemp
>>>
>>> tagged = f"{mkdtemp()}/tagged.bam"
>>> with (
... AlignmentFile("tests/data/reads.bam") as reads,
... AlignmentFile(tagged, "wb", template=reads) as writer,
... StreamingPileupBuilder(reads, tap=writer.write) as builder,
... ):
... for entry in builder.pileup("chr1", 10).pileups:
... if entry.base not in (None, "A"):
... entry.alignment.set_tag("XV", entry.base)
>>>
>>> with AlignmentFile(tagged) as written:
... for read in written:
... if read.has_tag("XV"):
... print(read.query_name, read.get_tag("XV"))
snv T
mnv G
Sweeping a Territory
The columns method yields the pileup at every position of a span.
Its views, such as bases, qualities, and the depths, are computed in Rust, so prefer them to looping over pileups in Python, which is where the time goes at depth.
>>> from collections import Counter
>>>
>>> with (
... AlignmentFile("tests/data/reads.bam") as reads,
... StreamingPileupBuilder(reads, min_base_quality=30) as builder,
... ):
... for pileup in builder.columns("chr1", 9, 13):
... print(pileup.reference_pos, dict(Counter(pileup.bases)))
9 {'A': 4}
10 {'A': 2, 'T': 1, 'G': 1}
11 {'C': 3, 'G': 1}
12 {'G': 3}
Each pileup is a snapshot that outlives the builder moving on.
Tabulating Alleles
The tabulate function counts the reads of every allele at every base of a bedspec Territory.
Alleles are normalized VCF alleles at 1-based positions, so they match a VCF by CHROM, POS, REF, and ALT.
>>> from bedspec import Bed3
>>> from bedspec import Territory
>>> from pysam import FastaFile
>>> from streampile import tabulate
>>>
>>> territory = Territory([Bed3("chr1", start=9, end=12)])
>>> with (
... AlignmentFile("tests/data/reads.bam") as reads,
... FastaFile("tests/data/reference.fa") as reference,
... ):
... bases = list(tabulate(reads, reference, territory, min_base_quality=30))
>>>
>>> for base in bases:
... print(base.pos, base.ref, base.alts, base.alt_reads, base.alt_fwd, base.alt_rev)
10 A () () () ()
11 A ('T', 'GG') (1, 1) (1, 0) (0, 1)
12 C () () () ()
The alt_fwd and alt_rev fields split each allele's reads by strand, and no_calls counts N bases apart from the depth.
Writing a Table
A path ending in .gz is written with BGZF, indexed as it is written when given an index, and metadata adds ##key=value lines above the header:
>>> from pybgzf import IndexFormat
>>> from streampile import TabulationWriter
>>>
>>> table = f"{mkdtemp()}/counts.tsv.gz"
>>> with TabulationWriter.from_path(
... table, index=IndexFormat.TBI, metadata={"min_base_quality": 30}
... ) as writer:
... writer.write_all(bases)
Reading a Table
>>> from streampile import TabulationReader
>>>
>>> for base in TabulationReader.from_path(table):
... print(base.pos, base.depth, base.alts, base.alt_reads)
10 4 () ()
11 4 ('T', 'GG') (1, 1)
12 4 () ()
With the index, a region can be read alone; it is half-open and 0-based, as in BED:
>>> for base in TabulationReader.query(table, "chr1", 10, 11):
... print(base.pos, base.depth, base.alts, base.alt_reads)
11 4 ('T', 'GG') (1, 1)
Command Line
streampile tabulate \
--bam tests/data/reads.bam \
--ref tests/data/reference.fa \
--intervals tests/data/territory.bed \
--min-base-quality 30 \
--min-mapping-quality 20 \
--out counts.tsv.gz \
--index tbi
This writes the table compressed with BGZF and its tabix index, counts.tsv.gz.tbi; decompressed, it reads:
##streampile-tabulation=1
##streampile-version=0.2.0
##bam=tests/data/reads.bam
##reference=tests/data/reference.fa
##territory=tests/data/territory.bed
##min_base_quality=30
##min_mapping_quality=20
##exclude_flags=0xf00
#contig pos ref depth no_calls ref_reads ref_fwd ref_rev alt_refs alts alt_reads alt_fwd alt_rev
chr1 21 T 5 0 5 3 2
chr1 22 G 5 0 5 3 2
chr1 23 C 4 0 3 2 1 CA C 1 0 1
chr1 24 A 4 0 3 2 1
The --threads option sets the threads decompressing the BAM and compressing a BGZF table: by default, the CPUs available, at most 8.
The examples read tests/data/reads.bam, whose reads are named for what they carry, such as plain, snv, mnv, lowqual, insertion, and deletion.
Development and Testing
See the contributing guide for more information.
The streaming design follows the StreamingPileupBuilder of fgbio of which I was also the author.
See NOTICE for official attribution.
Metadata
Release files for streampile 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| streampile-0.2.0.tar.gz | 173.1 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| streampile-0.2.0-cp311-abi3-musllinux_1_2_x86_64.whl | CPython 3.11 | abi3 | Linux musl 1.2+ x86-64 | Details |
| streampile-0.2.0-cp311-abi3-musllinux_1_2_aarch64.whl | CPython 3.11 | abi3 | Linux musl 1.2+ ARM64 | Details |
| streampile-0.2.0-cp311-abi3-manylinux_2_28_aarch64.whl | CPython 3.11 | abi3 | Linux glibc 2.28+ ARM64 | Details |
| streampile-0.2.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.11 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| streampile-0.2.0-cp311-abi3-macosx_11_0_arm64.whl | CPython 3.11 | abi3 | macOS 11.0+ ARM64 | Details |
| streampile-0.2.0-cp311-abi3-macosx_10_12_x86_64.whl | CPython 3.11 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 3.6 MB
Release files / streampile-0.2.0.tar.gz
| Download URL | streampile-0.2.0.tar.gz |
|---|---|
| Size | 173.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a7d06e59d46329b0382bd9895b30cb836760bac7d4600b0196649b532b488204
|
|
BLAKE2b-256 checksum How to use checksums |
c7d119b67a5e92725065fdcd48b6630ad9597a299c68d2f06b5156e26802c6ac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency logRelease files / streampile-0.2.0-cp311-abi3-musllinux_1_2_x86_64.whl
| Download URL | streampile-0.2.0-cp311-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 736.4 kB |
| Tags | CPython 3.11 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
7c88cecce38c95feac06efda8547386bf51e18a575c4b2e98ce0c4dfbaeaf5af
|
|
BLAKE2b-256 checksum How to use checksums |
1043c650a96b6127470cc40fec3ab39093b8636cab97df1bc5a4288afff33d05
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency logRelease files / streampile-0.2.0-cp311-abi3-musllinux_1_2_aarch64.whl
| Download URL | streampile-0.2.0-cp311-abi3-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 690.3 kB |
| Tags | CPython 3.11 Linux musl 1.2+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
216e967657adfaa4bb9885db629d3b0d17f308cdc7760b612cecc84283e470d1
|
|
BLAKE2b-256 checksum How to use checksums |
c66628c6a5cab08812f17c549b90a4dd4d97780451784972b963a23aefdaa94f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency logRelease files / streampile-0.2.0-cp311-abi3-manylinux_2_28_aarch64.whl
| Download URL | streampile-0.2.0-cp311-abi3-manylinux_2_28_aarch64.whl |
|---|---|
| Size | 511.3 kB |
| Tags | CPython 3.11 Linux glibc 2.28+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
66d976309c1624ab174ecd9affa69280e4f82b2ea1ccff15c21b84c67ec37e9a
|
|
BLAKE2b-256 checksum How to use checksums |
143c9607c269b6d307902a64f1b5415ff5e26da2f0781626d9fdfaa7870123bc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency logRelease files / streampile-0.2.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | streampile-0.2.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 521.5 kB |
| Tags | CPython 3.11 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
4f3412136c75e6d15410044c558f3a8c435ac9ac3e54cb6211e54ca46bd8aebe
|
|
BLAKE2b-256 checksum How to use checksums |
eed046bb4c1b44e8c4048e90d1daad29140d9b24bd68289bfd2de15ffb684d61
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency logRelease files / streampile-0.2.0-cp311-abi3-macosx_11_0_arm64.whl
| Download URL | streampile-0.2.0-cp311-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 478.0 kB |
| Tags | CPython 3.11 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
cfcebfe6af5a126e1f3a31e75abe30bdc708342f0aeb4f9db8fb081f9f9474a7
|
|
BLAKE2b-256 checksum How to use checksums |
7a21f735e6be3ed875acb8c047cf725383798a0af2f2ca833b385f19bcc8b493
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency logRelease files / streampile-0.2.0-cp311-abi3-macosx_10_12_x86_64.whl
| Download URL | streampile-0.2.0-cp311-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 484.9 kB |
| Tags | CPython 3.11 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
3bf1cef4efbae842ea6c608c3ec5b4457b0eff865b193eabe52c339719959fa9
|
|
BLAKE2b-256 checksum How to use checksums |
23fc95fbc402cfe931b7086b424d43f81c45c165a0a2ad5a95c213a920f7dba6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency log