Skip to main content

rna-gc-bias-metrics

Measure GC-content bias in RNA-seq coverage.

Given a transcriptome FASTA and a BAM of reads aligned to it, rna-gc-bias-metrics computes per-bin GC content and coverage depth, then summarizes how normalized coverage varies with GC fraction. This reveals whether a library is over- or under-covering GC-rich (or GC-poor) regions — a common artifact of library preparation and sequencing chemistry.

How it works

For each transcript the pipeline:

  1. Reads the BAM (via polars-bio) and drops unmapped, secondary, and supplementary alignments, as well as paired reads that are not properly paired (mate-unmapped or discordant mates). Single-end reads and proper pairs are kept.
  2. Expands each read's CIGAR into the reference positions it covers, and deduplicates the overlap between properly paired mates so shared bases are counted once.
  3. Collapses coverage into a per-range bedgraph, then redistributes it into fixed-length bins (default 100 bp), splitting partial-bin coverage proportionally.
  4. Computes each bin's GC fraction from the transcript sequence and normalizes its depth against the transcript's mean bin depth (so 1.0 is the transcript average).
  5. Aggregates mean normalized depth and bin counts per rounded GC fraction across the whole input.

Requirements

Dependencies are managed with either uv (PyPI) or pixi (conda + PyPI). Pick whichever you already use.

Installation

git clone git@github.com:nebiolabs/rna-gc-bias-metrics.git
cd rna-gc-bias-metrics

# with uv
uv sync

# or with pixi
pixi install

Input requirements

  • Transcriptome FASTA with single-line (unwrapped) sequences — one line of sequence per record. Wrapped FASTA is not parsed correctly.
  • A FASTA index named <name>.fa.fai alongside it (created with samtools faidx).
  • A BAM aligned to that transcriptome (each RNAME is a transcript ID). BAM reference names must match the FASTA names, or the run aborts with an error.

Every reference name — in the FASTA, the .fa.fai and the BAM — is taken as the header up to the first whitespace, so descriptive headers need no cleanup:

>ENST00000227525.8 cdna chromosome:GRCh38:12:6534517:6538371:1 gene:ENSG00000111640.15

is matched against a BAM RNAME of ENST00000227525.8. This mirrors the aligners (the SAM spec forbids whitespace in RNAME, so only the leading word survives alignment) and samtools faidx, which truncates the same way.

To prepare a wrapped FASTA:

seqkit seq -w 0 transcripts.fa > transcripts.unwrapped.fa   # unwrap sequences
samtools faidx transcripts.unwrapped.fa                      # create .fa.fai

Usage

The tool runs as a module. Under uv, prefix commands with uv run; under pixi, use pixi run.

uv run python -m rna_gc_bias_metrics.calculate_gc_coverage \
    transcripts.fa \
    reads.bam \
    --report_bin_count_for_full_transcriptome \
    -o gc_bias_profile.tsv

Arguments

Argument Description
fp_fasta Path to the transcriptome FASTA (indexed; see input requirements).
fp_bam Path to the BAM of reads aligned to the transcriptome.
-o, --outfp Output file path (default: stdout).
--fixed_length_bin_bp Bin size in base pairs (default: 100).
--report_bin_count_for_full_transcriptome Also report, per GC fraction, the number of bins across every transcript in the FASTA (covered or not) — the background GC distribution to compare coverage against.

Output

A tab-separated table, one row per rounded GC fraction:

Column Meaning
gc_fraction GC fraction of the bin, rounded to two decimals (0–1).
mean_normalized_depth Mean within-transcript-normalized depth across covered bins at this GC fraction. 1.0 = transcript average; > 1 enriched, < 1 depleted.
transcriptome_bin_count Number of covered bins at this GC fraction.
transcriptome_bin_count_all_transcripts Bins at this GC fraction across all transcripts (only with --report_bin_count_for_full_transcriptome).

Example (run against the bundled test fixtures):

gc_fraction  mean_normalized_depth  transcriptome_bin_count  transcriptome_bin_count_all_transcripts
0.47         0.9900990099009903     1                        1
0.59         1.0099009900990097     1                        3
0.65         1.7894736842105265     1                        3
0.68         0.28947368421052655    1                        1
0.70         0.21052631578947384    1                        1
0.77         1.7105263157894732     1                        1

(GC fractions with no covered bins have empty depth/count columns but still appear when the full-transcriptome background is reported.)

Python API

The pipeline steps are importable for use in scripts or notebooks:

from rna_gc_bias_metrics.calculate_gc_coverage import (
    load_sequences,
    calculate_gc_coverage,
    calculate_gc_pct_coverage,
)

sequences, faidx = load_sequences("transcripts.fa", "transcripts.fa.fai")
bin_cov_with_gc = calculate_gc_coverage("reads.bam", sequences, faidx, fixed_length_bin_bp=100)

# mean normalized depth per rounded GC fraction
per_gc_coverage = calculate_gc_pct_coverage(bin_cov_with_gc).collect()

calculate_gc_coverage returns a per-bin table (rname, bin_start, depth_fractional, gc_frac, depth_normalized, gc_frac_rounded) as a Polars LazyFrame; call .collect() to materialize it. load_bam, expand_cigar, bam_to_bedgraph, get_binned_coverage, and get_bin_gc expose the individual stages.

Notes and limitations

  • Coverage is deduplicated across the overlap of properly paired mates so shared bases are counted once. Paired reads that are not properly paired (mate-unmapped or discordant mates) are dropped. Read-through ("dovetail") mate tails past the mate's end are dropped.
  • The bedgraph end coordinate carries a known off-by-one that can leak a small amount of coverage into the adjacent bin.

Development

uv run pytest test/ -v      # with uv
pixi run test               # with pixi

Tests should pass under both workflows. See AGENTS.md for project layout and details on keeping the two dependency ecosystems in sync.

License

Distributed under the GNU Affero General Public License v3.0. See LICENSE.txt.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rna_gc_bias_metrics-0.1.1.tar.gz (23.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rna_gc_bias_metrics-0.1.1-py3-none-any.whl (22.9 kB view details)

Uploaded Python 3

File details

Details for the file rna_gc_bias_metrics-0.1.1.tar.gz.

File metadata

  • Download URL: rna_gc_bias_metrics-0.1.1.tar.gz
  • Upload date:
  • Size: 23.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Rocky Linux","version":"9.5","id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for rna_gc_bias_metrics-0.1.1.tar.gz
Algorithm Hash digest
SHA256 1c5af203a4b35c110ada3d81fe76f12fe2f80d7355821465a750872178dbe842
MD5 fa0984831155cf6198b058d2b6942c74
BLAKE2b-256 ef035c4f264f8417e361d48e9d1caf8556e3a7703e28eab3fb748e291d63695d

See more details on using hashes here.

File details

Details for the file rna_gc_bias_metrics-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: rna_gc_bias_metrics-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 22.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Rocky Linux","version":"9.5","id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for rna_gc_bias_metrics-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e4c8ec8142f1854faaecf9ccae346e83837db82f89ad3049fb651de7055675ca
MD5 be49c09c466f10daf95010ba23393efe
BLAKE2b-256 13abeb18b407c207df37ca87953413557c9a8e8050158536f7d085e6ae659fd2

See more details on using hashes here.

Release history Release notifications | RSS feed

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.2

2 files

This release

0.1.1 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page