agentsclimarketplace

Bio copy number cnvkit analysis skills cnvkit analysis

Skill bg-szy/TOP-SKILLS/skills/awesome-skills/bio-copy-number-cnvkit-analysis__skills-cnvkit-analysis

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.From its SKILL.md

Install
npx -y skills add bg-szy/TOP-SKILLS --skill bio-copy-number-cnvkit-analysis__skills-cnvkit-analysis

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

15.9 KB, ~4.0k tokens by cl100k_base, as published. Nobody here has run it

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

CallerSignal usedOutputPurity/ploidy awareFails when
CNVkitDepth (on + off-target)Relative log2, threshold-called CNNo (manual --purity)Sample purity < ~40%; hyper-aneuploid genome breaks median centering; balanced events invisible
GATK gCNV / somatic CNVDepth (PCA/tangent denoised)Copy-ratio segments, +/-/0 callNo (somatic); ploidy prior (germline)Recurrent CNV in the PoN normalized away; ModelSegments gives no integer ASCN
ASCAT / Sequenza / FACETSDepth + B-allele frequencyInteger allele-specific CN, purity, ploidyYes (jointly fit)Near-diploid genome cannot anchor purity; low het-SNP density
ExomeDepth / GATK gCNV (cohort)Depth across a cohortGermline CN genotypeGermline 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

ScenarioRecommended CNVkit configurationWhy
Hybrid-capture panel or exome, tumor-normalbatch hybrid mode, matched normal as referenceAntitargets recover off-target resolution; matched normal cancels capture bias
Exome cohort, pooled normals availableBuild pooled PoN reference, then batch --referencePooled reference averages out per-normal noise; 5-20+ normals
Amplicon / multiplex-PCR panelbatch --method ampliconNo usable off-target reads; antitarget bins are pure noise — must be dropped
Whole-genome sequencingbatch --method wgsGenome-wide fixed bins; no target/antitarget split
Tumor-only, no normal of any kindbatch with flat reference (omit --normal)Last resort; expect GC/capture-bias false positives — see failure mode below
FFPE / low-input / impure tumorAdd --drop-low-coverage; segment with hmm-tumorFFPE dropout produces zero-coverage bins that CBS reads as deletions
Need absolute CN, LOH, purityDo not use CNVkit aloneEscalate 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:

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.

# 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)

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.

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.

# 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. Per-probe capture bias is then misread as copy number.

Symptom: Recurrent "CNVs" at the same loci across unrelated tumor-only samples; spiky .cnr profiles; high MAD; calls concentrated at probe boundaries.

Fix: Never rely on a flat reference for clinical or focal calls. Build a pooled PoN from >= 5 process-matched normals. If truly no normal exists, treat tumor-only CNVkit output as hypothesis-generating only and escalate to PureCN (allele-specific-copy-number), which models a normal database explicitly.

Antitarget bins on amplicon panels — pure noise

Trigger: Running default hybrid mode on an amplicon (multiplex-PCR) panel.

Mechanism: Amplicon panels produce essentially no off-target reads. Antitarget bins then contain a handful of stray reads, giving wildly variable log2 that the segmenter chases.

Symptom: Huge antitarget bin spread; nonsensical genome-wide segments between the targeted genes.

Fix: Use --method amplicon, which drops antitargets entirely and calls only from on-target bins. Accept that resolution is limited to the targeted genes.

Low tumor purity — the death zone below ~40%

Trigger: Tumor cellularity below ~40% (common in breast, lung adenocarcinoma, melanoma, low-cellularity biopsies).

Mechanism: Each somatic CN change is diluted by 2-copy normal DNA. A true single-copy loss at 30% purity produces log2 ~ -0.23 — inside the diploid threshold band.

Symptom: Genome looks near-flat; few or no calls; known driver amplifications (e.g. ERBB2, MYC) missed.

Fix: CNVkit cannot rescue this. Confirm purity with an allele-specific caller (BAF gives an orthogonal purity estimate). Below ~20% purity, no depth-based caller is reliable — report as indeterminate.

Hyper-aneuploid / whole-genome-doubled genome — baseline miscalled

Trigger: Tumor with >50% of the genome altered, or whole-genome doubling.

Mechanism: call --center median (or mode) assumes the commonest log2 state is diploid. In a WGD genome the commonest state is tetraploid; centering on it shifts the whole profile and inverts gain/loss calls.

Symptom: Genome-wide pattern of calls inconsistent with known biology; "deletions" everywhere or "gains" everywhere.

Fix: Do not trust depth-only centering on aneuploid tumors. Anchor the diploid baseline with BAF/SNV data via an allele-specific caller, which estimates absolute ploidy directly.

FFPE / low-input dropout read as homozygous deletions

Trigger: Degraded FFPE DNA or low input; some bins have near-zero coverage.

Mechanism: Zero-coverage bins produce extreme negative log2; CBS joins them into spurious homozygous-deletion segments.

Symptom: Scattered tiny "CN=0" segments, often at hard-to-capture (high-GC) loci.

Fix: Always pass --drop-low-coverage to batch and segment. Inspect MAD; if MAD > 0.5, the sample is too noisy for confident focal calls.

Reconciliation: When CNVkit Disagrees With Another Caller

PatternLikely causeAction
CNVkit calls focal events GATK missesGATK PoN/tangent normalization absorbed the eventTrust CNVkit if the event is rare; suspect GATK if PoN contained tumors
GATK/ASCAT call broad arm events CNVkit flattensCNVkit centered on a non-diploid modeRe-center against an allele-specific ploidy estimate
CNVkit and ASCAT disagree on integer CNCNVkit purity guess wrong, or genome is WGDTrust ASCAT/FACETS — joint BAF+depth fit resolves purity/ploidy
Tumor-only CNVkit calls absent in matched-normal rerunGermline CNV or capture bias misread as somaticRe-run with the matched normal; germline CNVs are not somatic events

Operational rule for high-confidence reporting: Treat a CNVkit call as confident only when (1) the reference was a pooled PoN of process-matched normals, (2) sample MAD < 0.5, (3) the segment spans multiple bins with consistent weight, and (4) for any clinically actionable focal event, it is confirmed by an orthogonal caller or by allele-specific data. Depth-only calls on tumors are screening-grade, not definitive.

Quality Control

cnvkit.py metrics results/*.cnr -s results/*.cns      # MAD, spread, bivar per sample
cnvkit.py sex results/*.cnr                           # detect sex / sample swaps
cnvkit.py segmetrics tumor.cnr -s tumor.cns --ci --pi --bootstrap 100 -o tumor.segmetrics.cns
cnvkit.py genemetrics tumor.cnr -s tumor.cns -t 0.2 --ci --bootstrap 100 -o tumor.genemetrics.tsv

Quantitative Thresholds

ThresholdValueSource / Rationale
Sample MAD (noise)< 0.5 acceptable; < 0.3 goodCNVkit docs; MAD is the median absolute deviation of bin log2
Panel of normals size>= 5; 10-20 preferredTalevich 2016; pooling averages per-normal capture noise
Default call thresholds (log2)-1.1, -0.25, 0.2, 0.7CNVkit call -t defaults: CN 0 / 1 / 2 / 3 / 4+ boundaries
Purity floor for depth calling~40% reliable; ~20% absolute floorBelow ~40% segmentation fails (sCNAphase, Gusnanto 2012)
genemetrics -t gain/loss0.2 (default)2^0.2 ~ 15% copy-ratio change; tune up for impure samples
Antitarget avg bin sizeauto; ~target size x fold-enrichmentCNVkit docs; off-target bins should hold comparable read counts

Common Errors

Error / symptomCauseSolution
Spiky .cnr, recurrent calls across samplesFlat reference; capture bias misreadBuild a pooled PoN; never use flat reference for focal calls
Antitarget spread huge, nonsense segmentsHybrid mode on an amplicon panelUse --method amplicon
Scattered CN=0 micro-segmentsFFPE zero-coverage binsAdd --drop-low-coverage to batch and segment
Integer CN systematically too lowcall without -m clonal --puritySupply purity, or use an allele-specific caller
Whole genome called gain or lossCentering on a non-diploid mode (WGD)Anchor ploidy with BAF; do not depth-center aneuploid tumors
pomegranate ImportError on HMMHMM backend not installedpip install pomegranate; or use -m cbs

References

  • Talevich E et al 2016. CNVkit: genome-wide copy number detection from targeted DNA sequencing. PLoS Comput Biol 12:e1004873
  • Olshen AB et al 2004. Circular binary segmentation for the analysis of array-based DNA copy number data. Biostatistics 5:557 (CBS)
  • Gusnanto A et al 2012. Correcting for cancer genome size and tumour cell content in whole-genome copy number. Bioinformatics 28:40
  • Benjamini Y, Speed TP 2012. Summarizing and correcting the GC content bias in high-throughput sequencing. Nucleic Acids Res 40:e72

Related Skills

  • copy-number/copy-ratio-segmentation - CBS vs HMM choice, depth normalization, bias correction
  • copy-number/allele-specific-copy-number - ASCAT/Sequenza/FACETS/PureCN for purity, ploidy, integer ASCN
  • copy-number/gatk-cnv - GATK depth-based alternative; tangent normalization
  • copy-number/cnv-annotation - Gene and clinical annotation of CNV calls
  • copy-number/cnv-visualization - Profile plots, segmentation views, cohort heatmaps
  • copy-number/recurrent-cnv - GISTIC2 cohort-level recurrent and driver CNV
  • alignment-files/bam-statistics - QC of input BAMs before calling
  • long-read-sequencing/structural-variants - Complementary breakpoint-resolved SV calling

What ships with it: 1 file

4.2 KB alongside SKILL.md

Keep looking

Skills are one crate of 326,834. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.