CNV cohort (TCGA)
TL;DR: tumors from different patients tend to gain and lose the same regions, because those regions carry a gene driving the cancer. We stack copy-number segment calls for 1104 TCGA breast tumors, one row per tumor colored by gain or loss, so a recurrent event reads as a vertical stripe down the stack.
Prerequisites
- A JBrowse 2 instance to add tracks to (see the web quickstart, or the desktop quickstart, which loads these tracks by URL with nothing to host) and the JBrowse CLI
- These files, hosted; the whole 1104-tumor cohort is a few MB of segment calls:
| File | What |
|---|---|
https://jbrowse.org/demos/tcga/tcga_brca_cnv.bed.gz | the segment stack |
https://jbrowse.org/demos/tcga/tcga_brca_cnv_recurrence.bedGraph.gz | cohort gain/loss frequencies |
https://jbrowse.org/demos/tcga/tcga_brca_cnv_recurrence_by_subtype.bedGraph.gz | the same, split by clinical group |
https://jbrowse.org/demos/tcga/tcga_brca_clinical.tsv | per-tumor histology, receptors |
Where the data comes from
TCGA-BRCA, from the GDC's open-access Masked Copy Number Segment files (Affymetrix SNP 6.0, harmonized to GRCh38), so no dbGaP application or token is needed.
- primary-tumor segment calls for 1104 tumors, queried and downloaded through the GDC API: https://api.gdc.cancer.gov/files
- per-tumor clinical annotation, from harmonized case fields and each case's clinical XML: https://api.gdc.cancer.gov/cases
- the segment stack, rehosted so the figures and their live links load without the GDC round trip: https://jbrowse.org/demos/tcga/tcga_brca_cnv.bed.gz
- the cohort recurrence track and the same split by clinical group: https://jbrowse.org/demos/tcga/tcga_brca_cnv_recurrence.bedGraph.gz and https://jbrowse.org/demos/tcga/tcga_brca_cnv_recurrence_by_subtype.bedGraph.gz
- the clinical table the stack is grouped by: https://jbrowse.org/demos/tcga/tcga_brca_clinical.tsv
The hg38 reference and gene track beside them are the hosted UCSC hub's own entries.
What the files hold
Reproduce it end to end below builds these files from the GDC for any project id.
The BED is one segment call per line, with a #-prefixed header naming the
columns past end:
#chrom start end name sample segmean
chr1 3301764 30796057 +0.15 TCGA-3C-AAAU-01A 0.1480
chr1 3301764 7589655 -0.98 TCGA-3C-AALI-01A -0.9761
sample is a TCGA barcode and splits the rows; segmean is the caller's log2
tumor/normal ratio and colors them.
Load the segments into JBrowse
The assembly comes first. The hosted FASTA calls its contigs bare (1) while
the BED uses chr1, so pass the alias file and both resolve.
export OUT=/var/www/html/jbrowse2
jbrowse add-assembly https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz \
--name hg38 --type bgzipFasta \
--refNameAliases https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/hg38_aliases.txt \
--out $OUT
The segments themselves are a FeatureTrack whose
LinearMultiRowFeatureDisplay carries the row and color settings:
{
"type": "FeatureTrack",
"trackId": "tcga_brca_cnv",
"name": "TCGA-BRCA copy number (1104 primary tumors)",
"assemblyNames": ["hg38"],
"category": ["TCGA"],
"adapter": {
"type": "BedTabixAdapter",
"uri": "https://jbrowse.org/demos/tcga/tcga_brca_cnv.bed.gz"
},
"displays": [
{
"type": "LinearMultiRowFeatureDisplay",
"partitionField": "sample",
"color": "jexl:feature.segmean<-1?'#2166ac':feature.segmean<-0.3?'#92c5de':feature.segmean<0.3?'#f7f7f7':feature.segmean<1?'#f4a582':'#b2182b'",
"legend": [
{ "label": "Deep loss (log2 < -1)", "color": "#2166ac" },
{ "label": "Loss", "color": "#92c5de" },
{ "label": "Balanced", "color": "#f7f7f7" },
{ "label": "Gain", "color": "#f4a582" },
{ "label": "Amplification (log2 > 1)", "color": "#b2182b" }
]
}
]
}
jbrowse add-track-json '{
"type": "FeatureTrack",
"trackId": "tcga_brca_cnv",
"name": "TCGA-BRCA copy number (1104 primary tumors)",
"assemblyNames": ["hg38"],
"category": ["TCGA"],
"adapter": {
"type": "BedTabixAdapter",
"uri": "https://jbrowse.org/demos/tcga/tcga_brca_cnv.bed.gz"
},
"displays": [
{
"type": "LinearMultiRowFeatureDisplay",
"partitionField": "sample",
"color": "jexl:feature.segmean<-1?'\''#2166ac'\'':feature.segmean<-0.3?'\''#92c5de'\'':feature.segmean<0.3?'\''#f7f7f7'\'':feature.segmean<1?'\''#f4a582'\'':'\''#b2182b'\''",
"legend": [
{ "label": "Deep loss (log2 < -1)", "color": "#2166ac" },
{ "label": "Loss", "color": "#92c5de" },
{ "label": "Balanced", "color": "#f7f7f7" },
{ "label": "Gain", "color": "#f4a582" },
{ "label": "Amplification (log2 > 1)", "color": "#b2182b" }
]
}
]
}'
In JBrowse Desktop, or in any running JBrowse Web session, open a view on this track’s assembly, then File → Open track..., choose Add track from pasted JSON, and paste:
{
"type": "FeatureTrack",
"trackId": "tcga_brca_cnv",
"name": "TCGA-BRCA copy number (1104 primary tumors)",
"assemblyNames": ["hg38"],
"category": ["TCGA"],
"adapter": {
"type": "BedTabixAdapter",
"uri": "https://jbrowse.org/demos/tcga/tcga_brca_cnv.bed.gz"
},
"displays": [
{
"type": "LinearMultiRowFeatureDisplay",
"partitionField": "sample",
"color": "jexl:feature.segmean<-1?'#2166ac':feature.segmean<-0.3?'#92c5de':feature.segmean<0.3?'#f7f7f7':feature.segmean<1?'#f4a582':'#b2182b'",
"legend": [
{ "label": "Deep loss (log2 < -1)", "color": "#2166ac" },
{ "label": "Loss", "color": "#92c5de" },
{ "label": "Balanced", "color": "#f7f7f7" },
{ "label": "Gain", "color": "#f4a582" },
{ "label": "Amplification (log2 > 1)", "color": "#b2182b" }
]
}
]
}
rowHeight
auto-fits, which at this row count leaves every tumor a single pixel line. Three
settings do the rest:
partitionFieldsplits the file into one labeled row persamplecoloris a jexl expression binningsegmeanonto a diverging blue-to-red scale, since this BED carries noitemRgblegendnames each bin's log2 cutoff and color
Cluster the stack
Open the track at whole-genome zoom, then run Clustering → Cluster rows by similarity from the track menu (see Clustering rows), which sorts the stack into blocks of shared copy-number profile.
A vertical stripe is one locus called the same way across many rows. A whole row tending red or blue is one heavily aneuploid tumor, and clustering pulls those together into a band. Clustering on a single-locus window instead sorts the cohort into its copy-number classes there.
Every figure below is in the sorted state.
At this row count each row is well under a pixel tall, so the saturated colors crowd out the neutral ones. The stack maps where the events are; the track below counts how many rows carry them.
Add a recurrence track
Each 100 kb bin of tcga_brca_cnv_recurrence.bedGraph.gz carries the percent of
the cohort gained and the percent lost, on the same log2 cutoffs the stack
colors by (gain above 0.3, loss below -0.3):
#chrom start end gain loss
chr1 204700000 204800000 58.88 -1.36
chr8 127600000 127800000 49.73 -0.91
chr16 89200000 89300000 3.26 -46.38
BedGraphTabixAdapter reads every column past end as its own signal. Loss is
written negative so a wiggle's bicolorPivot at 0 draws gains up in posColor
and losses down in negColor.
{
"type": "QuantitativeTrack",
"trackId": "tcga_brca_cnv_recurrence",
"name": "TCGA-BRCA recurrence (% of 1104 tumors)",
"assemblyNames": ["hg38"],
"category": ["TCGA"],
"adapter": {
"type": "BedGraphTabixAdapter",
"uri": "https://jbrowse.org/demos/tcga/tcga_brca_cnv_recurrence.bedGraph.gz"
},
"displayDefaults": {
"height": 120,
"posColor": "#b2182b",
"negColor": "#2166ac",
"minScore": -100,
"maxScore": 100
}
}
jbrowse add-track-json '{
"type": "QuantitativeTrack",
"trackId": "tcga_brca_cnv_recurrence",
"name": "TCGA-BRCA recurrence (% of 1104 tumors)",
"assemblyNames": ["hg38"],
"category": ["TCGA"],
"adapter": {
"type": "BedGraphTabixAdapter",
"uri": "https://jbrowse.org/demos/tcga/tcga_brca_cnv_recurrence.bedGraph.gz"
},
"displayDefaults": {
"height": 120,
"posColor": "#b2182b",
"negColor": "#2166ac",
"minScore": -100,
"maxScore": 100
}
}'
In JBrowse Desktop, or in any running JBrowse Web session, open a view on this track’s assembly, then File → Open track..., choose Add track from pasted JSON, and paste:
{
"type": "QuantitativeTrack",
"trackId": "tcga_brca_cnv_recurrence",
"name": "TCGA-BRCA recurrence (% of 1104 tumors)",
"assemblyNames": ["hg38"],
"category": ["TCGA"],
"adapter": {
"type": "BedGraphTabixAdapter",
"uri": "https://jbrowse.org/demos/tcga/tcga_brca_cnv_recurrence.bedGraph.gz"
},
"displayDefaults": {
"height": 120,
"posColor": "#b2182b",
"negColor": "#2166ac",
"minScore": -100,
"maxScore": 100
}
}
minScore and maxScore
(display options) pin
the axis, so a bar means the same fraction wherever you navigate.
posColor/negColor reuse the stack's colors. Placed above the stack, each
peak sits over a stripe:
Each bar is the fraction of the cohort carrying a call past the cutoff, with no background model or significance test; GISTIC is the tool for that.
Split the recurrence by clinical group
cnv_recurrence.py --groups runs the same tally once per value of a clinical
column and writes each group its own gain and loss column:
curl -fO https://raw.githubusercontent.com/GMOD/jbrowse-components/main/scripts/cnv_recurrence.py
python3 cnv_recurrence.py tcga_brca_cnv.bed.gz by_subtype.bedGraph \
--groups tcga_brca_clinical.tsv:subtype
The --groups file is the same
clinical TSV
the mutation cohort uses. The eight columns arrive as eight signals, and a
MultiQuantitativeTrack draws
one row each:
{
"type": "MultiQuantitativeTrack",
"trackId": "tcga_brca_cnv_recurrence_by_subtype",
"name": "TCGA-BRCA recurrence by receptor subtype",
"assemblyNames": ["hg38"],
"category": ["TCGA"],
"adapter": {
"type": "BedGraphTabixAdapter",
"uri": "https://jbrowse.org/demos/tcga/tcga_brca_cnv_recurrence_by_subtype.bedGraph.gz"
},
"displayDefaults": {
"height": 620,
"posColor": "#b2182b",
"negColor": "#2166ac",
"minScore": -70,
"maxScore": 70,
"showRowSeparators": true
}
}
jbrowse add-track-json '{
"type": "MultiQuantitativeTrack",
"trackId": "tcga_brca_cnv_recurrence_by_subtype",
"name": "TCGA-BRCA recurrence by receptor subtype",
"assemblyNames": ["hg38"],
"category": ["TCGA"],
"adapter": {
"type": "BedGraphTabixAdapter",
"uri": "https://jbrowse.org/demos/tcga/tcga_brca_cnv_recurrence_by_subtype.bedGraph.gz"
},
"displayDefaults": {
"height": 620,
"posColor": "#b2182b",
"negColor": "#2166ac",
"minScore": -70,
"maxScore": 70,
"showRowSeparators": true
}
}'
In JBrowse Desktop, or in any running JBrowse Web session, open a view on this track’s assembly, then File → Open track..., choose Add track from pasted JSON, and paste:
{
"type": "MultiQuantitativeTrack",
"trackId": "tcga_brca_cnv_recurrence_by_subtype",
"name": "TCGA-BRCA recurrence by receptor subtype",
"assemblyNames": ["hg38"],
"category": ["TCGA"],
"adapter": {
"type": "BedGraphTabixAdapter",
"uri": "https://jbrowse.org/demos/tcga/tcga_brca_cnv_recurrence_by_subtype.bedGraph.gz"
},
"displayDefaults": {
"height": 620,
"posColor": "#b2182b",
"negColor": "#2166ac",
"minScore": -70,
"maxScore": 70,
"showRowSeparators": true
}
}
Row order follows the file's columns: four gain rows in red, then four loss rows in blue. The bottom row of each block is the tumors whose receptor calls do not resolve a subtype.
minScore/maxScore
pin the rows to one axis, narrower than the pooled track since each row fills
only half its axis. Gain and loss stay separate columns, since at the edge of
the 17q amplicon the HER2+ group is gained and lost at nearly the same rate.
--min-group sets how many tumors a subtype needs before it is plotted; the
script names each group it dropped. Point --groups at any other column for a
different split. histology and stage work for any TCGA project, while
subtype is breast specific.
Use your own cohort
Any caller that emits per-sample segments works. The track config only needs a BED with a sample column and a numeric column to color by:
#chrom start end name sample segmean
CNVkit .call.cns, ASCAT, and
PURPLE segments
all reshape into that with the same concatenate-and-tag step.
Where to go next
The same one-row-per-sample pattern carries to other GDC open-access data:
- Allele-specific copy number (ASCAT, open access) reports major and minor
allele copy number separately, so it shows copy-neutral loss of
heterozygosity. Same
.segshape, same display, only the coloring expression changes - Methylation (Beta Value arrays, open access) is probe-level with genomic coordinates, and loads the same way with beta as the color field
Splice junction quantification is controlled access at the GDC; GTEx and recount3 publish open junction summaries.
Reproduce it end to end
One script builds every file above for any project id:
build_tcga_cohort_cnv.sh,
which summarizes recurrence with
cnv_recurrence.py.
It needs curl, python3, and bgzip + tabix from
htslib, which on Debian/Ubuntu is
apt install curl python3 tabix.
curl -fO https://raw.githubusercontent.com/GMOD/jbrowse-components/main/scripts/build_tcga_cohort_cnv.sh
bash build_tcga_cohort_cnv.sh TCGA-BRCA 20 # 20 tumors, to test the pipeline
bash build_tcga_cohort_cnv.sh TCGA-BRCA # the full cohort, ~20 minutes
npx --yes serve jbrowse2 # then open the printed URL
It writes tcga_brca_cnv.bed.gz (+ .tbi), the two recurrence bedGraphs (+
.tbi) and tcga_brca_clinical.tsv, then a jbrowse2/ opening on ERBB2. The
assembly is the hosted UCSC hg38 hub's entry copied in, so the reference is
never downloaded. Swap in any other project id (TCGA-OV, TCGA-LUAD, ...),
with a third argument to group the recurrence by a different clinical column.
Three steps decide whether the track loads correctly:
- Open-access files only. The Masked Copy Number Segment files
(Affymetrix SNP 6.0, harmonized to GRCh38, germline CNV probes removed) need
no dbGaP application. The query filters to
Primary Tumor - The
.segto BED reshape..segnames contigs bare (1), so the script addschr, and.segstarts are 1-based inclusive, so it subtracts 1. It keeps one file per barcode Segment_Meanis carried through unchanged
cnv_recurrence.py
runs on its own given a cohort BED. It skips bins where fewer than half the
cohort has any call, over the whole cohort even with --groups, so the grouped
file has the same gaps as the pooled one. The clinical table comes from
tcga_clinical_tsv.py,
shared with the mutation cohort.
See also
- Multi-row feature track
- User guide: Quantitative track
- Mutation cohort (TCGA)
- QTL mapping (BXD mice)
- ChromHMM chromatin states
- CNV across a population (1000 Genomes)
- Structural variants (Cancer GIAB)
- Using jexl callbacks
References
Feedback on this tutorial is welcome: contact us.