Claude
Skills
Sign in
Back

bio-copy-number-cnvkit-analysis

Included with Lifetime
$97 forever

Detect somatic and germline copy number variants from targeted, exome, and whole-genome sequencing with CNVkit, a read-depth caller that combines on-target and off-target (antitarget) coverage. Covers panel-of-normals construction, flat-reference tumor-only calling, hybrid/amplicon/WGS modes, CBS vs HMM segmentation selection, purity-aware integer calling, and reconciliation against GATK and allele-specific callers. Use when calling CNVs from hybrid-capture panels or exomes, deciding whether CNVkit (depth-only) is the right tool versus an allele-specific caller, building a panel of normals, diagnosing flat-reference false positives, or interpreting log2 ratios into copy-number states.

Writing & Docs

What this skill does


## Version Compatibility

Reference examples tested with: CNVkit 0.9.10+, samtools 1.19+, bedtools 2.31+, Python 3.10+, R 4.3+ with DNAcopy 1.76+.

Before using code patterns, verify installed versions match. If versions differ:
- CLI: `cnvkit.py version` then `cnvkit.py batch --help` to confirm flags
- Python: `pip show cnvkit` then `python -c "import cnvlib; help(cnvlib.read)"`
- R: `packageVersion('DNAcopy')` (CBS backend)

If a command throws an unrecognized-argument or AttributeError, introspect the installed version and adapt the example rather than retrying. CNVkit segmentation methods (`hmm`, `hmm-tumor`, `hmm-germline`) depend on `pomegranate`; CBS depends on Bioconductor `DNAcopy`.

# CNVkit Copy Number Analysis

**"Detect copy number variants from my exome / panel data"** -> Run a read-depth pipeline: normalize on-target and off-target coverage against a reference, segment the log2-ratio profile, and call gains/losses. CNVkit is a *depth-only* caller — it estimates **relative** copy number and cannot, on its own, resolve tumor purity, ploidy, or allele-specific state. Choosing CNVkit is a decision that the experiment does not require allelic resolution.

- CLI: `cnvkit.py batch tumor.bam --normal normal.bam --targets panel.bed --fasta ref.fa`
- Python API: `cnvlib.read('sample.cnr')` for downstream filtering

## Where CNVkit Sits — Caller Taxonomy

| Caller | Signal used | Output | Purity/ploidy aware | Fails when |
|--------|-------------|--------|---------------------|------------|
| CNVkit | Depth (on + off-target) | Relative log2, threshold-called CN | No (manual `--purity`) | Sample purity < ~40%; hyper-aneuploid genome breaks median centering; balanced events invisible |
| GATK gCNV / somatic CNV | Depth (PCA/tangent denoised) | Copy-ratio segments, +/-/0 call | No (somatic); ploidy prior (germline) | Recurrent CNV in the PoN normalized away; ModelSegments gives no integer ASCN |
| ASCAT / Sequenza / FACETS | Depth + B-allele frequency | Integer allele-specific CN, purity, ploidy | Yes (jointly fit) | Near-diploid genome cannot anchor purity; low het-SNP density |
| ExomeDepth / GATK gCNV (cohort) | Depth across a cohort | Germline CN genotype | Germline ploidy only | < ~30-100 technically matched samples; common CNV |

CNVkit's niche: a fast, single-sample depth caller for hybrid-capture panels and exomes where antitarget reads recover genome-wide resolution. Its limit: it answers "is this region gained or lost relative to baseline" — not "how many absolute copies, on which haplotype, in what fraction of cells." For tumor integer CN, purity, LOH, or whole-genome doubling, escalate to allele-specific-copy-number.

## Decision Tree by Scenario

| Scenario | Recommended CNVkit configuration | Why |
|----------|----------------------------------|-----|
| Hybrid-capture panel or exome, tumor-normal | `batch` hybrid mode, matched normal as reference | Antitargets recover off-target resolution; matched normal cancels capture bias |
| Exome cohort, pooled normals available | Build pooled PoN reference, then `batch --reference` | Pooled reference averages out per-normal noise; 5-20+ normals |
| Amplicon / multiplex-PCR panel | `batch --method amplicon` | No usable off-target reads; antitarget bins are pure noise — must be dropped |
| Whole-genome sequencing | `batch --method wgs` | Genome-wide fixed bins; no target/antitarget split |
| Tumor-only, no normal of any kind | `batch` with flat reference (omit `--normal`) | Last resort; expect GC/capture-bias false positives — see failure mode below |
| FFPE / low-input / impure tumor | Add `--drop-low-coverage`; segment with `hmm-tumor` | FFPE dropout produces zero-coverage bins that CBS reads as deletions |
| Need absolute CN, LOH, purity | Do not use CNVkit alone | Escalate to allele-specific-copy-number (ASCAT/Sequenza/FACETS/PureCN) |

## Core Pipeline — Tumor-Normal Pair

The `batch` command wraps target/antitarget generation, coverage, reference building, fix, and segment:

```bash
cnvkit.py batch tumor.bam \
    --normal normal.bam \
    --targets panel.bed \
    --annotate refFlat.txt \
    --fasta reference.fa \
    --access access-excludes.bed \
    --output-reference reference.cnn \
    --output-dir results/ \
    --drop-low-coverage \
    --diagram --scatter
```

`--access` restricts antitarget bins to mappable, non-gap genome (generate once with `cnvkit.py access reference.fa -o access.bed`). `--drop-low-coverage` is effectively mandatory for tumor, FFPE, or any sample with coverage dropout.

## Panel of Normals — The Reference Determines Call Quality

A reference built from pooled normals is the single largest quality lever. Process is: build the reference from normals once, then run every tumor against it.

```bash
# Build pooled reference from process-matched normals (same capture kit, same lab)
cnvkit.py batch --normal normal*.bam \
    --targets panel.bed --annotate refFlat.txt --fasta reference.fa \
    --access access.bed \
    --output-reference pooled_reference.cnn

# Run each tumor against the pre-built reference
cnvkit.py batch tumor*.bam --reference pooled_reference.cnn \
    --output-dir results/ --drop-low-coverage --scatter --diagram
```

## Step-by-Step Pipeline (Fine-Grained Control)

```bash
cnvkit.py target panel.bed --annotate refFlat.txt --split -o targets.bed
cnvkit.py antitarget panel.bed --access access.bed -o antitargets.bed
cnvkit.py coverage tumor.bam targets.bed -o tumor.targetcoverage.cnn
cnvkit.py coverage tumor.bam antitargets.bed -o tumor.antitargetcoverage.cnn
cnvkit.py reference normal*.{target,antitarget}coverage.cnn --fasta reference.fa -o reference.cnn
cnvkit.py fix tumor.targetcoverage.cnn tumor.antitargetcoverage.cnn reference.cnn -o tumor.cnr
cnvkit.py segment tumor.cnr -o tumor.cns --drop-low-coverage
cnvkit.py call tumor.cns -o tumor.call.cns
```

## Segmentation Method Selection

CNVkit's `segment` step is where the bias-variance trade-off is set. The default CBS is not always correct — see copy-ratio-segmentation for the full algorithm comparison.

```bash
cnvkit.py segment tumor.cnr -m cbs -o tumor.cns          # default; precise on focal events
cnvkit.py segment tumor.cnr -m hmm-tumor -o tumor.cns    # heterogeneous tumor, broad states
cnvkit.py segment tumor.cnr -m hmm-germline -o tumor.cns # germline, priors near diploid
cnvkit.py segment tumor.cnr -m haar -o tumor.cns         # fast, low-depth WGS
```

Rule of thumb: CBS for panels/exomes with adequate depth (precise on small segments); `hmm-tumor` for impure or heterogeneous tumors where CBS over-fragments; `haar` for shallow WGS where CBS recall degrades.

## Purity-Aware Integer Calling

`call` converts segmented log2 ratios to copy-number states. The `clonal` method rescales by tumor purity before rounding to integers — without it, an impure tumor's true CN=4 amplification rounds to CN=3 or CN=2.

```bash
# Threshold method (default): fixed log2 cutpoints, no purity correction
cnvkit.py call tumor.cns -o tumor.call.cns

# Clonal method: rescale by purity, then round to integer CN
cnvkit.py call tumor.cns -m clonal --purity 0.65 --ploidy 2 -o tumor.call.cns

# Overlay B-allele frequency from a SNV VCF (for LOH visualization, NOT joint ASCN)
cnvkit.py call tumor.cns -m clonal --purity 0.65 --vcf tumor.vcf.gz -o tumor.call.cns
```

CNVkit can read BAF from a VCF and report a `baf` column, but it segments log2 and BAF *separately* and does not jointly fit purity from them. For a true joint allele-specific model (ASPCF, FACETS joint segmentation), use allele-specific-copy-number.

## Failure Modes

### Flat reference (tumor-only) — systematic false focal calls

**Trigger:** No `--normal` and no pooled PoN; CNVkit builds a flat reference (uniform log2 0) from the FASTA.

**Mechanism:** A flat reference corrects only GC and (optionally) RepeatMasker content via the FASTA. It cannot correct capture efficiency, which varies 10-100x across probes and is the dominant bias in hybrid capture

Related in Writing & Docs