Skip to content
Back to skills

Bio Core Chromatogram Analysis

ASecurity

Sanger sequencing chromatogram analysis — reading .ab1 files with BioPython, Phred quality scores, trace visualization, and quality-based trimming

  • 4 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added May 27, 2026
data-aipythongoapi

Works with

  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add Pavel-Kravchenko/Bioinformatics --skill bio-core-chromatogram-analysis --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Bio Core Chromatogram Analysis?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Bio Core Chromatogram Analysis
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/pavel-kravchenko-bio-core-chromatogram-analysis/badge)](https://www.skillsdirectory.com/skills/pavel-kravchenko-bio-core-chromatogram-analysis)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: bio-core-chromatogram-analysis
description: Sanger sequencing chromatogram analysis — reading .ab1 files with BioPython, Phred quality scores, trace visualization, and quality-based trimming
tool_type: python
primary_tool: NumPy
---

# Chromatogram Analysis: Sanger Sequencing

## Phred Quality Score Reference

| Q Score | Error Probability | Accuracy | Interpretation |
|:-------:|:-----------------:|:--------:|---------------|
| 10 | 1/10 | 90% | Poor — avoid |
| 20 | 1/100 | 99% | Minimum acceptable |
| 30 | 1/1,000 | 99.9% | High confidence |
| 40 | 1/10,000 | 99.99% | Excellent |

Formula: Q = -10 × log₁₀(P_error)

Practical thresholds: Q<20 = trim/re-sequence; Q20-29 = acceptable; Q≥30 = high confidence.

## Sanger Read Quality Profile

- Bases 1–25: poor quality (primer artifact, dye blobs) — always trim
- Bases 25–700: high quality plateau (Q>30 typical)
- Bases 700+: signal decay, quality drops sharply — trim tail

## Reading .ab1 Files

```python
from Bio import SeqIO
import numpy as np

record = SeqIO.read("sample.ab1", "abi")

# Basic info
sequence = str(record.seq)
quals = record.letter_annotations["phred_quality"]  # list of ints

# Raw trace data
raw = record.annotations["abif_raw"]
base_order = raw.get("FWO_1", b"GATC").decode()  # e.g. "GATC"

# Extract processed trace channels
channels = {}
for i, base in enumerate(base_order):
    channels[base] = np.array(raw[f"DATA{9 + i}"])   # DATA9..DATA12

peak_locs = list(raw["PLOC1"])   # scan positions of called bases
```

### Key .ab1 Tags

| Tag | Content |
|-----|---------|
| `DATA9`–`DATA12` | Processed trace channels (order from `FWO_1`) |
| `DATA1`–`DATA4` | Raw trace channels |
| `PLOC1` / `PLOC2` | Peak locations (scan numbers) |
| `PBAS1` / `PBAS2` | Called bases (raw / edited) |
| `PCON1` / `PCON2` | Quality values |
| `FWO_1` | Base order for channels (e.g. `b'GATC'`) |

Note: `.ab1` and `.abi` are the same format; `.scf` is an older MRC-LMB format. BioPython reads all via `"abi"`.

## Trace Visualization

```python
import matplotlib.pyplot as plt

TRACE_COLORS = {"A": "green", "C": "blue", "G": "black", "T": "red"}

def plot_chromatogram(channels, peak_locs, sequence, start=0, end=50):
    scan_start = peak_locs[start] - 10
    scan_end = peak_locs[min(end, len(peak_locs)) - 1] + 10
    x = np.arange(scan_start, scan_end)

    fig, ax = plt.subplots(figsize=(14, 3.5))
    for base, color in TRACE_COLORS.items():
        ax.plot(x, channels[base][scan_start:scan_end], color=color, lw=0.8, label=base)

    for i in range(start, min(end, len(peak_locs))):
        base = sequence[i]
        ax.text(peak_locs[i], 0, base, ha="center", fontsize=7,
                fontweight="bold", color=TRACE_COLORS.get(base, "gray"))
    ax.legend(ncol=4, fontsize=8)
    ax.set_xlabel("Scan position"); ax.set_ylabel("Fluorescence")
    plt.tight_layout(); plt.show()
```

## Quality Trimming

```python
def trim_by_quality(sequence, quals, min_q=20, window=10):
    """Trim low-quality ends using sliding window."""
    # Find first window where mean quality >= min_q (5' end)
    start = 0
    for i in range(len(quals) - window + 1):
        if np.mean(quals[i:i+window]) >= min_q:
            start = i
            break
    # Find last window (3' end)
    end = len(quals)
    for i in range(len(quals) - window, -1, -1):
        if np.mean(quals[i:i+window]) >= min_q:
            end = i + window
            break
    return sequence[start:end], quals[start:end]
```

## Pitfalls

- **Double peaks = heterozygosity OR contamination**: two overlapping peaks of comparable height at one position — check surrounding context; genuine het SNPs form a consistent pattern across the read; contamination creates irregular double peaks throughout
- **First 20–30 bases always low quality**: this is normal (primer signal + dye artifacts) — never report variants from this region
- **Why 4 dyes**: each ddNTP (ddA, ddC, ddG, ddT) carries a different fluorescent dye; capillary electrophoresis separates by size, laser detects dye — the instrument reconstructs sequence from size+color
- **`.ab1` stores raw fluorescence, not just sequence**: the base calls are computed by KB Basecaller software; you can re-call from the raw traces with third-party tools
- **`DATA9`–`DATA12` are processed; `DATA1`–`DATA4` are raw**: use processed channels for visualization (they have baseline correction and color deconvolution applied)
- **Sanger max read length ~800 bp**: beyond this, band resolution collapses — design primers to place your region of interest in the Q>30 window (bases ~30–700)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…