Claude
Skills
Sign in
Back

bio-copy-number-allele-specific-copy-number

Included with Lifetime
$97 forever

Infer integer allele-specific copy number, tumor purity, and ploidy from tumor sequencing by jointly modeling read depth (logR) and B-allele frequency (BAF) with ASCAT, Sequenza, FACETS, PURPLE, and PureCN (tumor-only). Covers the purity-ploidy identifiability problem, the diploid-baseline (dipLogR) anchor, major/minor copy number, loss of heterozygosity, sunrise/contour fit diagnostics, and reconciliation of conflicting fits. Use when tumor analysis needs absolute copy number rather than relative log2, when estimating purity and ploidy, calling LOH or copy-neutral LOH, resolving whole-genome doubling, running tumor-only allele-specific calling, or choosing among ASCAT, Sequenza, FACETS, and PureCN.

Writing & Docs

What this skill does


## Version Compatibility

Reference examples tested with: ASCAT 3.1+, Sequenza 3.0+ (sequenza-utils 3.0+), FACETS 0.6+ (snp-pileup), PureCN 2.6+, R 4.3+, Python 3.10+.

Before using code patterns, verify installed versions match. If versions differ:
- R: `packageVersion('ASCAT')` / `'sequenza'` / `'facets'` / `'PureCN'`, then `?function`
- CLI: `sequenza-utils --version`, `snp-pileup --help`

Sequenza 3.0 depends on the `copynumber` Bioconductor package, REMOVED from Bioconductor 3.18+ (2023). Install a maintained fork (`ShixiangWang/copynumber` or `igordot/copynumber`) before Sequenza will load. ASCAT's GC-correction function was renamed across 2.x->3.x (`ascat.GCcorrect` -> `ascat.correctLogR`) — verify against the installed version.

# Allele-Specific Copy Number

**"How many copies of each allele, in what fraction of cells, at what tumor purity"** -> Jointly model read depth and B-allele frequency to fit tumor purity, ploidy, and integer major/minor copy number per segment. Depth alone gives only *relative* copy ratio; depth + BAF gives *absolute* allele-specific copy number. This skill is required whenever the question involves LOH, absolute copy number, purity, ploidy, or whole-genome doubling — CNVkit and GATK somatic CNV cannot answer those.

- R: `ASCAT` (WGS, SNP array), `sequenza` (WES/WGS), `facets` (panel/WES/WGS), `PureCN` (tumor-only panel/WES)
- CLI: `purple` (Hartwig WGS pipeline, with AMBER + COBALT)

## The Identifiability Problem — Why This Is Hard

Purity and ploidy are **not identifiable from depth alone**. The same log-ratio profile is explained equally well by many (purity, ploidy) pairs: a homozygous deletion at 30% purity looks identical to a heterozygous deletion at 60% purity; an entire profile can be reinterpreted at 2x ploidy with halved purity. Every allele-specific caller breaks this degeneracy by adding BAF — allelic imbalance constrains which solution is real. The consequence: the likelihood surface is **multimodal**, the fit can lock onto an integer-multiple of the true ploidy, and a single point estimate must never be trusted without inspecting the fit diagnostic (ASCAT sunrise plot, Sequenza cellularity/ploidy contour, FACETS dipLogR). A real published example: the same tumor scored ploidy 4.27 by FACETS (WGS) and 2.42 by ASCAT (SNP array).

## Caller Taxonomy

| Tool | Input | Segmentation | Best for | Fails when |
|------|-------|--------------|----------|------------|
| ASCAT | SNP array or WGS logR+BAF | ASPCF (allele-specific PCF) | WGS, SNP6, large cohorts | Near-diploid genome with few aberrations cannot anchor purity -> defaults toward purity ~100% |
| Sequenza | Tumor-normal WES/WGS (seqz) | `copynumber` PCF | Exome, accessible install | Picks a near-diploid local optimum; needs manual review of alternative solutions |
| FACETS | Tumor-normal, snp-pileup | Joint logR+BAF CBS | Targeted panels, WES, clinical NGS | `cval` too low -> hyperfragmentation; EM locks onto an integer-multiple ploidy |
| PURPLE | WGS, AMBER+COBALT+SVs | Integrates SV breakpoints | WGS with matched SV calls | Targeted/WES (designed for WGS); needs the Hartwig tool stack |
| PureCN | Tumor-only WES/panel + PoN | Coverage + VCF, normal DB | No matched normal | Sparse hets; small panels; needs a well-built normal database |
| Battenberg | WGS logR+BAF, phased | ASCAT-based clonal + subclonal | Subclonal CN, clonal evolution | Heavy; needs phasing reference — see subclonal-copy-number |

## Decision Tree by Data Type

| Scenario | Recommended caller | Rationale |
|----------|--------------------|-----------|
| Tumor-normal WGS | ASCAT or PURPLE | PURPLE if SV calls available (resolves breakpoints); ASCAT otherwise |
| Tumor-normal WES | Sequenza or FACETS | Both joint logR+BAF; FACETS faster, Sequenza reports alternative solutions |
| Targeted panel (tumor-normal) | FACETS | Designed for panel het density; clinical-NGS standard |
| Tumor-only panel / WES | PureCN | Models a normal database; the standard tumor-only solution |
| SNP array (legacy) | ASCAT | ASCAT was built for SNP arrays |
| Subclonal CN / clonal evolution | Battenberg / TITAN | See subclonal-copy-number |
| Only relative gain/loss needed | CNVkit / GATK | Allele-specific machinery is unnecessary |

## FACETS — Tumor-Normal Allele-Specific CN

**Goal:** Fit purity, ploidy, and integer allele-specific CN for a panel or WES pair.

**Approach:** Pile up read counts at common SNPs with `snp-pileup`, then run the two-pass FACETS workflow — a high-`cval` purity run whose `dipLogR` seeds a low-`cval` sensitivity run for focal events.

```bash
# Step 1: pileup at dbSNP common sites (normal first, then tumor)
snp-pileup -g -q15 -Q20 -P100 -r25,0 dbsnp_common.vcf.gz \
    sample.snp_pileup.csv.gz normal.bam tumor.bam
```

```r
library(facets)
set.seed(1234)                                  # FACETS uses random initialization
rcmat <- readSnpMatrix('sample.snp_pileup.csv.gz')
xx <- preProcSample(rcmat)                      # gbuild default 'hg19'; pass gbuild='hg38' for GRCh38

# Pass 1: purity/ploidy at a coarse cval (panels ~150-300; WGS ~25-100)
oo1 <- procSample(xx, cval = 300)
fit1 <- emcncf(oo1)

# Pass 2: focal sensitivity, seeded by the diploid baseline from pass 1
oo2 <- procSample(xx, cval = 150, dipLogR = oo1$dipLogR)
fit2 <- emcncf(oo2)

cat('purity', fit2$purity, 'ploidy', fit2$ploidy, 'dipLogR', oo2$dipLogR, '\n')
# fit2$cncf has per-segment tcn.em (total CN) and lcn.em (minor CN); lcn.em == 0 -> LOH
plotSample(x = oo2, emfit = fit2)               # ALWAYS inspect this diagnostic plot
```

## Sequenza — Exome Tumor-Normal

**Goal:** Estimate cellularity/ploidy and allele-specific CN from a WES pair, with explicit alternative solutions.

**Approach:** Build a `seqz` file from the BAMs, bin it, then run the extract/fit/results chain; inspect the cellularity/ploidy contour and the reported alternative solutions.

```bash
sequenza-utils bam2seqz -n normal.bam -t tumor.bam --fasta ref.fa \
    -gc hg38.gc50.wig.gz -o sample.seqz.gz
sequenza-utils seqz_binning --seqz sample.seqz.gz -w 50 -o sample.bin.seqz.gz
```

```r
library(sequenza)
seqz <- sequenza.extract('sample.bin.seqz.gz')
CP <- sequenza.fit(seqz)                        # grid search over cellularity x ploidy
sequenza.results(seqz, CP, 'sampleID', out.dir = 'sequenza_out')
# Inspect *_CP_contours.pdf and *_alternative_solutions.txt before accepting the fit.
```

## ASCAT — WGS / SNP Array

**Goal:** Fit purity (rho), ploidy (psi), and allele-specific CN genome-wide.

**Approach:** Load logR/BAF, correct for GC (and optionally replication timing), segment with ASPCF, run the ASCAT fit, and read the sunrise plot.

```r
library(ASCAT)
ascat.bc <- ascat.loadData('Tumor_LogR.txt', 'Tumor_BAF.txt',
                           'Germline_LogR.txt', 'Germline_BAF.txt')
ascat.bc <- ascat.correctLogR(ascat.bc, GCcontentfile = 'GC_G1000.txt',
                              replictimingfile = 'RT_G1000.txt')   # RT optional
ascat.bc <- ascat.aspcf(ascat.bc)
ascat.output <- ascat.runAscat(ascat.bc, gamma = 1)   # gamma=1 for NGS; ~0.55 for arrays
# ascat.output$purity, $ploidy, $goodnessOfFit; $nA / $nB are major/minor CN per segment
# Inspect the sunrise plot: banding at multiples of ploidy signals an ambiguous fit.
```

## PureCN — Tumor-Only

**Goal:** Recover purity, ploidy, allele-specific CN, and LOH without a matched normal.

**Approach:** Build a normal database (PoN) once, then run `runAbsoluteCN` with the tumor coverage and a VCF; PureCN uses the normal DB and a mapping-bias model in place of a matched normal.

```r
library(PureCN)
ret <- runAbsoluteCN(
    tumor.coverage.file = 'tumor_coverage.txt.gz',
    vcf.file = 'tumor.vcf.gz',
    normalDB = readRDS('normalDB.rds'),       # built once from >= ~20 process-matched normals
    genome = 'hg38', sampleid = 'tumor',
    interval.file = 'baits_intervals.txt')
# ret$results[[1]]$purity / $ploidy; createCurationFile() flags fits needing manual review
```

## Failure Mod

Related in Writing & Docs