CNV cohort (TCGA)
The tutorials target the JBrowse v5 beta, and the v4.3.0 release
on the download page lacks some of what they show. To install the
beta of JBrowse Web, run npm install -g @jbrowse/cli@next, then
jbrowse create jbrowse2 --branch v5.0.0-beta.13. Desktop beta builds are coming soon.
Tumors from different patients tend to gain and lose the same regions, because those regions contain 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 shows as a vertical stripe.
Prerequisites
- a JBrowse 2 instance to add tracks to (see the web quickstart or the desktop quickstart) and the JBrowse CLI
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.
The build script fetches these files, so there is nothing to download by hand.
- primary-tumor segment calls for 1104 tumors, queried and downloaded through the GDC API: api.gdc.cancer.gov/fileshttps://api.gdc.cancer.gov/files
- per-tumor clinical annotation, from harmonized case fields and each case's clinical XML: api.gdc.cancer.gov/caseshttps://api.gdc.cancer.gov/cases
The build script copies the hg38 reference and the MANE gene track from the hosted UCSC hg38 hub's config, so it downloads no reference; the fence below loads GRCh38 from jbrowse.org.
The segment BED format, one call per line
The segment BED holds one copy-number call per line, with a #-prefixed header
naming the columns past end.
Reproduce it end to end builds it from the GDC.
#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.9761sample is a TCGA barcode that splits the rows, and segmean is the caller's
log2 tumor/normal ratio that colors them.
Load the segments into JBrowse
Add the assembly first. The hosted FASTA names its contigs 1, 2, ... and the
BED uses chr1, so pass the alias file to map one onto the other.
Goes in the assemblies array of config.json. See Assemblies.
{
"name": "hg38",
"uri": "https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz",
"refNameAliases": {
"uri": "https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/hg38_aliases.txt"
},
"cytobands": "https://jbrowse.org/genomes/GRCh38/cytoBand.txt"
}jbrowse add-assembly https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz \
--name hg38 \
--refNameAliases https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/hg38_aliases.txt \
--config '{"cytobands":"https://jbrowse.org/genomes/GRCh38/cytoBand.txt"}'hg38 is one of the genomes JBrowse Desktop hosts, with gene tracks already set up: on the start screen click Show all available genomes and pick it. To load the files of this config instead:
In JBrowse Desktop, Open new genome on the start screen (or File → Open genome... in a session), then Open from a URL and paste, one per line:
https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz
https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz.fai
https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz.gziJBrowse reads the format off the file name. Then fill in:
- Genome name:
hg38 - refName aliases (under More options):
https://s3.amazonaws.com/jbrowse.org/genomes/GRCh38/hg38_aliases.txt - cytobands (under More options):
https://jbrowse.org/genomes/GRCh38/cytoBand.txt
The segments themselves are a FeatureTrack whose
LinearMultiRowFeatureDisplay holds the row and color settings:
Goes in the tracks array of config.json. See Tracks.
{
"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",
"rows": "sample",
"color": {
"field": "segmean",
"scale": "threshold",
"domain": ["-1", "-0.3", "0.3", "1"],
"range": ["#2166ac", "#92c5de", "#f7f7f7", "#f4a582", "#b2182b"],
"labels": [
"Deep loss (log2 < -1)",
"Loss",
"Balanced",
"Gain",
"Amplification (log2 > 1)"
],
"title": "Copy number (log2)"
}
}
]
}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",
"rows": "sample",
"color": {
"field": "segmean",
"scale": "threshold",
"domain": ["-1", "-0.3", "0.3", "1"],
"range": ["#2166ac", "#92c5de", "#f7f7f7", "#f4a582", "#b2182b"],
"labels": [
"Deep loss (log2 < -1)",
"Loss",
"Balanced",
"Gain",
"Amplification (log2 > 1)"
],
"title": "Copy number (log2)"
}
}
]
}'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",
"rows": "sample",
"color": {
"field": "segmean",
"scale": "threshold",
"domain": ["-1", "-0.3", "0.3", "1"],
"range": ["#2166ac", "#92c5de", "#f7f7f7", "#f4a582", "#b2182b"],
"labels": [
"Deep loss (log2 < -1)",
"Loss",
"Balanced",
"Gain",
"Amplification (log2 > 1)"
],
"title": "Copy number (log2)"
}
}
]
}Open this track in JBrowse Web ↗
Open this track in JBrowse Desktop ↗ (JBrowse Desktop 5.0+)
rowHeight
auto-fits, which at this row count makes each tumor under a pixel tall. In
grammar-of-graphics terms, two settings draw the rows and colors:
rowsassigns one labeled row per value ofsamplecoloris athresholdcolor scale onsegmean, since this BED has noitemRgb: the four cuts indomainopen five intervals,rangegives each a color on a diverging blue-to-red scale, andlabelsnames each interval in the key
Cluster tumors by copy-number profile
Open the track over ERBB2.
Choose Clustering → Cluster rows by similarity... in the track menu (see
Clustering rows). JBrowse averages segmean across each bin in
view and sorts the rows into blocks with a shared copy-number profile, which
gathers the tumors amplified at ERBB2 into one band.
Clustered at whole-genome zoom, the stack groups tumors by their genome-wide profile, and the heavily aneuploid tumors, whose rows run red or blue end to end, form a separate band.
Add a track of gain and loss frequency
Each 100 kb bin of tcga_brca_cnv_recurrence.bedGraph.gz has the percent of the
cohort gained and the percent lost, on the same log2 cutoffs the stack colors by
(gain at or above 0.3, loss at or 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.38BedGraphTabixAdapter reads every column past end as a separate signal. Loss
is written negative, so a threshold color scale with no domain, which cuts
at the default origin of 0, draws gains up in one color and losses down in the
other.
Goes in the tracks array of config.json. See Tracks.
{
"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,
"color": {
"field": "score",
"scale": "threshold",
"range": ["#2166ac", "#b2182b"]
},
"scales": { "y": { "domainMin": -100, "domainMax": 100 } }
}
}jbrowse add-track https://jbrowse.org/demos/tcga/tcga_brca_cnv_recurrence.bedGraph.gz \
--trackId tcga_brca_cnv_recurrence \
--name "TCGA-BRCA recurrence (% of 1104 tumors)" \
--assemblyNames hg38 \
--category TCGA \
--displayDefaults '{"height":120,"color":{"field":"score","scale":"threshold","range":["#2166ac","#b2182b"]},"scales":{"y":{"domainMin":-100,"domainMax":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,
"color": {
"field": "score",
"scale": "threshold",
"range": ["#2166ac", "#b2182b"]
},
"scales": { "y": { "domainMin": -100, "domainMax": 100 } }
}
}scales.y's domainMin and domainMax
(display options) pin
the axis, so a bar means the same fraction wherever you navigate. Place the
track above the stack, so that each peak sits over a stripe:
Split gain and loss frequency by clinical group
cnv_recurrence.py --groups runs the same tally once per value of a clinical
column, such as tumor subtype, and writes a gain and a loss column per group:
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 --min-group 20The --groups file is the
clinical TSV
the mutation cohort also uses. Four subtypes give eight signals, each named for
its subtype and direction, with losses below zero. We'll draw them on a
mark display, where a mark is the shape
drawn (here a bar) and rows assigns one row per subtype, with its gain above
the line and its loss below. A formula step reads the subtype off each
signal's column name:
Goes in the tracks array of config.json. See Tracks.
{
"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"
},
"displays": [
{
"type": "LinearMarkDisplay",
"height": 500,
"transform": [
{
"type": "formula",
"expr": "jexl:replace(replace(feature.source, ' gain', ''), ' loss', '')",
"as": "subtype"
}
],
"rows": {
"field": "subtype",
"domain": ["HR+/HER2-", "HER2+", "triple-negative", "unknown"]
},
"scales": {
"y": { "domainMin": -70, "domainMax": 70, "title": "% of tumors" }
},
"marks": [
{
"mark": "bar",
"encoding": {
"y": "score",
"color": {
"field": "score",
"scale": "threshold",
"domain": [0],
"range": ["#2166ac", "#b2182b"],
"labels": ["loss", "gain"],
"title": "Copy number call"
}
}
}
]
}
]
}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"
},
"displays": [
{
"type": "LinearMarkDisplay",
"height": 500,
"transform": [
{
"type": "formula",
"expr": "jexl:replace(replace(feature.source, '\'' gain'\'', '\'''\''), '\'' loss'\'', '\'''\'')",
"as": "subtype"
}
],
"rows": {
"field": "subtype",
"domain": ["HR+/HER2-", "HER2+", "triple-negative", "unknown"]
},
"scales": {
"y": { "domainMin": -70, "domainMax": 70, "title": "% of tumors" }
},
"marks": [
{
"mark": "bar",
"encoding": {
"y": "score",
"color": {
"field": "score",
"scale": "threshold",
"domain": [0],
"range": ["#2166ac", "#b2182b"],
"labels": ["loss", "gain"],
"title": "Copy number call"
}
}
}
]
}
]
}'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"
},
"displays": [
{
"type": "LinearMarkDisplay",
"height": 500,
"transform": [
{
"type": "formula",
"expr": "jexl:replace(replace(feature.source, ' gain', ''), ' loss', '')",
"as": "subtype"
}
],
"rows": {
"field": "subtype",
"domain": ["HR+/HER2-", "HER2+", "triple-negative", "unknown"]
},
"scales": {
"y": { "domainMin": -70, "domainMax": 70, "title": "% of tumors" }
},
"marks": [
{
"mark": "bar",
"encoding": {
"y": "score",
"color": {
"field": "score",
"scale": "threshold",
"domain": [0],
"range": ["#2166ac", "#b2182b"],
"labels": ["loss", "gain"],
"title": "Copy number call"
}
}
}
]
}
]
}Open this track in JBrowse Web ↗
Open this track in JBrowse Desktop ↗ (JBrowse Desktop 5.0+)
On a track already open:
- Display types → Marks draws one row per column.
- Advanced → Edit plot... adds the
formulastep and the rows.
The bottom row is the tumors with no resolved subtype.
The file keeps gain and loss as separate columns, since at the edge of the 17q amplicon the HER2+ group is gained and lost at nearly the same rate, and its row draws both.
Two options adjust the split:
--min-groupsets how many tumors a subtype needs to be plotted, and the script names each group it dropped.--groupstakes any other column for a different split:histologyandstagework for any TCGA project, whilesubtypeis breast specific.
Use your own cohort
Any caller that writes per-sample segments works. Reshape its output into a BED with one segment per line, a sample column and a numeric column to color by:
#chrom start end name sample segmean
chr1 1000000 2500000 +0.42 tumor_01 0.42
chr1 1000000 8000000 -0.75 tumor_02 -0.75
chr8 127000000 128500000 +1.30 tumor_01 1.30CNVkit .call.cns, ASCAT and
PURPLE segments
all reshape into this by concatenating the per-sample files with the sample name
added as a column. Sort, compress and index the result:
jbrowse sort-bed cohort.bed | bgzip > cohort.bed.gz
tabix cohort.bed.gzAdd the segment track config from
Load the segments into JBrowse with uri set
to cohort.bed.gz.
Reproduce it end to end
One script builds every file above for any project id,
build_tcga_cohort_cnv.sh:
- It asks the GDC for the project's open-access Masked Copy Number Segment files (Affymetrix SNP 6.0, GRCh38, germline CNV probes removed), which need no dbGaP application, from primary tumors only, so the matched normals stay out of the stack.
- It keeps one file per tumor barcode, so a tumor run twice on the array does not draw two overlapping sets of segments in one row.
- It joins every tumor's segments into one BED with the barcode as
sample, addingchrto the contig names and moving the 1-based.segstarts to BED's 0-based ones.Segment_Meanpasses through unchanged, so each row's color is the caller's log2 ratio. cnv_recurrence.pytallies the gained and lost share of the cohort per 100 kb bin, pooled and per clinical group. A tumor counts in a bin when its segment covers the bin's midpoint. Bins where fewer than half the tumors have any call are left out, so a gap in the track is missing data.
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 URLThe script writes a jbrowse2/ opening on ERBB2. Swap in any other project id
(TCGA-OV, TCGA-LUAD, ...), with a third argument to group the recurrence by
a different clinical column.
cnv_recurrence.py also runs alone on a cohort BED. The clinical table comes
from
tcga_clinical_tsv.py,
shared with the mutation cohort.
See also
- Tutorial: Mutation cohort (TCGA)
- Tutorial: QTL mapping (BXD mice)
- Tutorial: ChromHMM chromatin states
- Tutorial: CNV across a population (1000 Genomes)
- Tutorial: A grammar of graphics over a BED (RepeatMasker Alu age)
- Tutorial: Structural variants (Cancer GIAB)
- User guide: Multi-row feature track
- User guide: Quantitative track
- Config guide: Using jexl callbacks
External links
Feedback on this tutorial is welcome: contact us.