A grammar of graphics over a BED (RepeatMasker Alu age)
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.17. Desktop beta builds are coming soon.
RepeatMasker annotates Alu, a primate-specific repeat family, and its milliDiv
column records each copy's divergence from its subfamily consensus, in tenths of
a percent. Divergence grows with time, so the column is an age. We:
- plot each copy's
milliDivas a bar at one locus - bin and count copies when zoomed out
- plot the AluY share per megabase against the genome-wide share
- plot that share against Alu density across chromosome 1
The plots come from LinearMarkDisplay, which turns the columns of a feature
file into plots from a JSON marks list. It is experimental, and its config
shape may change.
Prerequisites
- a JBrowse to open the figures' sessions in (Web or Desktop)
- htslib (
bgzip,tabix) - Node.js and the JBrowse CLI, for
jbrowse make-density python3, standard library only, for the per-megabase countsbedGraphToBigWigfrom the UCSC utilities, whichmake-densityruns
# Debian/Ubuntu
sudo apt install tabix nodejs
# or conda, which has every tool above
conda install -c bioconda -c conda-forge htslib nodejs ucsc-bedgraphtobigwig
# or Linux x86_64 binaries for the tools apt lacks
wget -P ~/.local/bin \
https://hgdownload.soe.ucsc.edu/admin/exe/linux.x86_64/bedGraphToBigWig
chmod +x ~/.local/bin/bedGraphToBigWig
# any platform
npm install -g @jbrowse/cliThe data
UCSC's hg38 RepeatMasker table, rehosted by genomes.jbrowse.org's hg38 with the Alu rows cut out.
You don't need these files to follow the page: the track configs below read the hosted results. The build script fetches them to rebuild those results. Your own RepeatMasker BED and genome FASTA go through the same steps: the build script takes them as arguments.
| What it is | File |
|---|---|
RepeatMasker, UCSC's rmsk table as BED with a column header | rmsk.bed.gz |
| Reference lengths, for the bigWig header | hg38.chrom.sizes |
Plotting each Alu copy's divergence at one locus
We'll load hg38, the assembly the RepeatMasker coordinates are on.
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 track reads any BED-like file, bgzipped and tabix-indexed, with a # header
line naming the columns; a mark refers to a column by name. In
grammar-of-graphics terms, encoding maps
columns to a mark's channels, and color is a color scale from a field
through scale.domain to scale.range. The track below lists three marks:
- Per-copy bars (below
maxBpPerPx): abarper copy withmilliDivas the height. Aformulastep writes the first four characters of the name intolineage(AluJ, AluS, AluY: oldest to youngest, and AluY is still inserting), and the ordinalscale.domainfixes the legend order - Copies per bin (past
minBpPerPx): every Alu copy counted per bin, in grey - AluY per bin: the same count behind a
filterfor AluY, in red
Goes in the tracks array of config.json. See Tracks.
{
"type": "FeatureTrack",
"trackId": "alu_age",
"name": "Alu copies",
"assemblyNames": ["hg38"],
"adapter": {
"type": "BedTabixAdapter",
"uri": "https://jbrowse.org/demos/gene_density/Alu.bed.gz"
},
"displays": [
{
"type": "LinearMarkDisplay",
"marks": [
{
"mark": "bar",
"transform": [
{
"type": "formula",
"expr": "jexl:substring(feature.name, 0, 4)",
"as": "lineage"
}
],
"encoding": {
"y": "milliDiv",
"color": {
"field": "lineage",
"scale": {
"type": "ordinal",
"domain": ["AluJ", "AluS", "AluY", "FLAM", "FRAM"],
"range": ["#4575b4", "#fdae61", "#d73027", "#8c8c8c", "#8c8c8c"]
},
"legend": { "title": "Alu lineage" }
}
},
"maxBpPerPx": 100
},
{
"mark": "bar",
"transform": [
{ "type": "bin", "step": "auto" },
{
"type": "aggregate",
"groupby": ["start", "end"],
"ops": [{ "op": "count" }]
}
],
"encoding": { "color": { "value": "#c0c0c0" } },
"minBpPerPx": 100
},
{
"mark": "bar",
"transform": [
{
"type": "filter",
"expr": "jexl:startsWith(feature.name, 'AluY')"
},
{ "type": "bin", "step": "auto" },
{
"type": "aggregate",
"groupby": ["start", "end"],
"ops": [{ "op": "count" }]
}
],
"encoding": { "color": { "value": "#d73027" } },
"minBpPerPx": 100
}
]
}
]
}jbrowse add-track-json '{
"type": "FeatureTrack",
"trackId": "alu_age",
"name": "Alu copies",
"assemblyNames": ["hg38"],
"adapter": {
"type": "BedTabixAdapter",
"uri": "https://jbrowse.org/demos/gene_density/Alu.bed.gz"
},
"displays": [
{
"type": "LinearMarkDisplay",
"marks": [
{
"mark": "bar",
"transform": [
{
"type": "formula",
"expr": "jexl:substring(feature.name, 0, 4)",
"as": "lineage"
}
],
"encoding": {
"y": "milliDiv",
"color": {
"field": "lineage",
"scale": {
"type": "ordinal",
"domain": ["AluJ", "AluS", "AluY", "FLAM", "FRAM"],
"range": ["#4575b4", "#fdae61", "#d73027", "#8c8c8c", "#8c8c8c"]
},
"legend": { "title": "Alu lineage" }
}
},
"maxBpPerPx": 100
},
{
"mark": "bar",
"transform": [
{ "type": "bin", "step": "auto" },
{
"type": "aggregate",
"groupby": ["start", "end"],
"ops": [{ "op": "count" }]
}
],
"encoding": { "color": { "value": "#c0c0c0" } },
"minBpPerPx": 100
},
{
"mark": "bar",
"transform": [
{
"type": "filter",
"expr": "jexl:startsWith(feature.name, '\''AluY'\'')"
},
{ "type": "bin", "step": "auto" },
{
"type": "aggregate",
"groupby": ["start", "end"],
"ops": [{ "op": "count" }]
}
],
"encoding": { "color": { "value": "#d73027" } },
"minBpPerPx": 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": "FeatureTrack",
"trackId": "alu_age",
"name": "Alu copies",
"assemblyNames": ["hg38"],
"adapter": {
"type": "BedTabixAdapter",
"uri": "https://jbrowse.org/demos/gene_density/Alu.bed.gz"
},
"displays": [
{
"type": "LinearMarkDisplay",
"marks": [
{
"mark": "bar",
"transform": [
{
"type": "formula",
"expr": "jexl:substring(feature.name, 0, 4)",
"as": "lineage"
}
],
"encoding": {
"y": "milliDiv",
"color": {
"field": "lineage",
"scale": {
"type": "ordinal",
"domain": ["AluJ", "AluS", "AluY", "FLAM", "FRAM"],
"range": ["#4575b4", "#fdae61", "#d73027", "#8c8c8c", "#8c8c8c"]
},
"legend": { "title": "Alu lineage" }
}
},
"maxBpPerPx": 100
},
{
"mark": "bar",
"transform": [
{ "type": "bin", "step": "auto" },
{
"type": "aggregate",
"groupby": ["start", "end"],
"ops": [{ "op": "count" }]
}
],
"encoding": { "color": { "value": "#c0c0c0" } },
"minBpPerPx": 100
},
{
"mark": "bar",
"transform": [
{
"type": "filter",
"expr": "jexl:startsWith(feature.name, 'AluY')"
},
{ "type": "bin", "step": "auto" },
{
"type": "aggregate",
"groupby": ["start", "end"],
"ops": [{ "op": "count" }]
}
],
"encoding": { "color": { "value": "#d73027" } },
"minBpPerPx": 100
}
]
}
]
}Open this track in JBrowse Web ↗
Needs JBrowse Desktop 5.0 or newer. If nothing opens, copy the link and paste it into Desktop's Open JBrowse Web link..., on the start screen or under File → Session.
https://jbrowse.org/code/jb2/main/?config=https%3A%2F%2Fjbrowse.org%2Fucsc%2Fhg38%2Fconfig.json&session=spec-%7B%22sessionTracks%22%3A%5B%7B%22type%22%3A%22FeatureTrack%22%2C%22trackId%22%3A%22alu_age%22%2C%22name%22%3A%22Alu+copies%22%2C%22assemblyNames%22%3A%5B%22hg38%22%5D%2C%22adapter%22%3A%7B%22type%22%3A%22BedTabixAdapter%22%2C%22uri%22%3A%22https%3A%2F%2Fjbrowse.org%2Fdemos%2Fgene_density%2FAlu.bed.gz%22%7D%2C%22displays%22%3A%5B%7B%22type%22%3A%22LinearMarkDisplay%22%2C%22marks%22%3A%5B%7B%22mark%22%3A%22bar%22%2C%22transform%22%3A%5B%7B%22type%22%3A%22formula%22%2C%22expr%22%3A%22jexl%3Asubstring%28feature.name%2C+0%2C+4%29%22%2C%22as%22%3A%22lineage%22%7D%5D%2C%22encoding%22%3A%7B%22y%22%3A%22milliDiv%22%2C%22color%22%3A%7B%22field%22%3A%22lineage%22%2C%22scale%22%3A%7B%22type%22%3A%22ordinal%22%2C%22domain%22%3A%5B%22AluJ%22%2C%22AluS%22%2C%22AluY%22%2C%22FLAM%22%2C%22FRAM%22%5D%2C%22range%22%3A%5B%22%234575b4%22%2C%22%23fdae61%22%2C%22%23d73027%22%2C%22%238c8c8c%22%2C%22%238c8c8c%22%5D%7D%2C%22legend%22%3A%7B%22title%22%3A%22Alu+lineage%22%7D%7D%7D%2C%22maxBpPerPx%22%3A100%7D%2C%7B%22mark%22%3A%22bar%22%2C%22transform%22%3A%5B%7B%22type%22%3A%22bin%22%2C%22step%22%3A%22auto%22%7D%2C%7B%22type%22%3A%22aggregate%22%2C%22groupby%22%3A%5B%22start%22%2C%22end%22%5D%2C%22ops%22%3A%5B%7B%22op%22%3A%22count%22%7D%5D%7D%5D%2C%22encoding%22%3A%7B%22color%22%3A%7B%22value%22%3A%22%23c0c0c0%22%7D%7D%2C%22minBpPerPx%22%3A100%7D%2C%7B%22mark%22%3A%22bar%22%2C%22transform%22%3A%5B%7B%22type%22%3A%22filter%22%2C%22expr%22%3A%22jexl%3AstartsWith%28feature.name%2C+%27AluY%27%29%22%7D%2C%7B%22type%22%3A%22bin%22%2C%22step%22%3A%22auto%22%7D%2C%7B%22type%22%3A%22aggregate%22%2C%22groupby%22%3A%5B%22start%22%2C%22end%22%5D%2C%22ops%22%3A%5B%7B%22op%22%3A%22count%22%7D%5D%7D%5D%2C%22encoding%22%3A%7B%22color%22%3A%7B%22value%22%3A%22%23d73027%22%7D%7D%2C%22minBpPerPx%22%3A100%7D%5D%7D%5D%7D%5D%2C%22views%22%3A%5B%7B%22type%22%3A%22LinearGenomeView%22%2C%22assembly%22%3A%22hg38%22%2C%22loc%22%3A%22chr1%3A151%2C000%2C000-151%2C030%2C000%22%2C%22tracks%22%3A%5B%22alu_age%22%5D%7D%5D%7D&sessionName=JBrowse+docs%3A+Alu+copiesOpen chr1:151,000,000-151,030,000, 30 kb of 1q21, and click a bar to open its
row.
Counting Alu copies per bin when zoomed out
On a whole chromosome the track exceeds its fetch budget and shows a "region too large" message. A bigWig of copies per kilobase, counted once from the BED, covers that width:
# writes Alu.bed.density.bw beside the input, in 1 kb bins
# --chrom-sizes takes a two-column name and length table;
# --assembly genome.fa reads the lengths off the FASTA's .fai instead
jbrowse make-density Alu.bed.gz --chrom-sizes hg38.chrom.sizesAdd the bigWig as a track:
Goes in the tracks array of config.json. See Tracks.
{
"trackId": "alu_per_kb",
"name": "Alu copies per kb",
"assemblyNames": ["hg38"],
"uri": "https://jbrowse.org/demos/gene_density/Alu.bed.density.bw",
"displayDefaults": { "extent": "none" }
}jbrowse add-track https://jbrowse.org/demos/gene_density/Alu.bed.density.bw \
--trackId alu_per_kb \
--name "Alu copies per kb" \
--assemblyNames hg38 \
--displayDefaults '{"extent":"none"}'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:
{
"trackId": "alu_per_kb",
"name": "Alu copies per kb",
"assemblyNames": ["hg38"],
"uri": "https://jbrowse.org/demos/gene_density/Alu.bed.density.bw",
"displayDefaults": { "extent": "none" }
}Open this track in JBrowse Web ↗
Needs JBrowse Desktop 5.0 or newer. If nothing opens, copy the link and paste it into Desktop's Open JBrowse Web link..., on the start screen or under File → Session.
https://jbrowse.org/code/jb2/main/?config=https%3A%2F%2Fjbrowse.org%2Fucsc%2Fhg38%2Fconfig.json&session=spec-%7B%22sessionTracks%22%3A%5B%7B%22trackId%22%3A%22alu_per_kb%22%2C%22name%22%3A%22Alu+copies+per+kb%22%2C%22assemblyNames%22%3A%5B%22hg38%22%5D%2C%22uri%22%3A%22https%3A%2F%2Fjbrowse.org%2Fdemos%2Fgene_density%2FAlu.bed.density.bw%22%2C%22displayDefaults%22%3A%7B%22extent%22%3A%22none%22%7D%7D%5D%2C%22views%22%3A%5B%7B%22type%22%3A%22LinearGenomeView%22%2C%22assembly%22%3A%22hg38%22%2C%22loc%22%3A%22chr1%22%2C%22tracks%22%3A%5B%22alu_per_kb%22%5D%7D%5D%7D&sessionName=JBrowse+docs%3A+Alu+copies+per+kbAluY share per megabase, against the genome-wide share
The red AluY count is too small to read against a total that varies between bins. A script instead writes a BED with two log2 columns, the AluY share per megabase against the genome-wide share and the plus-strand share against one half as the control.
Fetch the script:
curl -fO https://raw.githubusercontent.com/GMOD/jbrowse-components/main/scripts/alu_young_share.py# columns: copies, young, plus, youngShare, youngLog2, strandLog2, one row per
# 1 Mb bin holding at least 50 copies; prints the rank correlation of each share
# against copies per bin
python3 alu_young_share.py Alu.bed.gz Alu.young_share.bed AluY
bgzip -f Alu.young_share.bed
tabix -f -p bed Alu.young_share.bed.gzAdvanced → Edit plot... in the track menu applies edits to the cut, colors and key names live. The config below sets them in the track:
Goes in the tracks array of config.json. See Tracks.
{
"type": "FeatureTrack",
"trackId": "alu_young_share",
"name": "AluY share per Mb, against the genome",
"uri": "https://jbrowse.org/code/jb2/main/test_data/alu_age/Alu.young_share.bed.gz",
"assemblyNames": ["hg38"],
"displays": [
{
"type": "LinearMarkDisplay",
"encoding": {
"y": {
"scale": { "domainMin": -1.5, "domainMax": 1.5 },
"axis": { "title": "AluY share, log2" }
}
},
"marks": [
{
"mark": "bar",
"encoding": {
"y": "youngLog2",
"color": {
"field": "youngLog2",
"scale": {
"type": "threshold",
"domain": [0],
"range": ["#4575b4", "#d73027"],
"names": ["below the genome-wide share", "above it"]
},
"legend": { "title": "AluY share" }
}
}
}
]
}
]
}jbrowse add-track-json '{
"type": "FeatureTrack",
"trackId": "alu_young_share",
"name": "AluY share per Mb, against the genome",
"uri": "https://jbrowse.org/code/jb2/main/test_data/alu_age/Alu.young_share.bed.gz",
"assemblyNames": ["hg38"],
"displays": [
{
"type": "LinearMarkDisplay",
"encoding": {
"y": {
"scale": { "domainMin": -1.5, "domainMax": 1.5 },
"axis": { "title": "AluY share, log2" }
}
},
"marks": [
{
"mark": "bar",
"encoding": {
"y": "youngLog2",
"color": {
"field": "youngLog2",
"scale": {
"type": "threshold",
"domain": [0],
"range": ["#4575b4", "#d73027"],
"names": ["below the genome-wide share", "above it"]
},
"legend": { "title": "AluY share" }
}
}
}
]
}
]
}'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": "alu_young_share",
"name": "AluY share per Mb, against the genome",
"uri": "https://jbrowse.org/code/jb2/main/test_data/alu_age/Alu.young_share.bed.gz",
"assemblyNames": ["hg38"],
"displays": [
{
"type": "LinearMarkDisplay",
"encoding": {
"y": {
"scale": { "domainMin": -1.5, "domainMax": 1.5 },
"axis": { "title": "AluY share, log2" }
}
},
"marks": [
{
"mark": "bar",
"encoding": {
"y": "youngLog2",
"color": {
"field": "youngLog2",
"scale": {
"type": "threshold",
"domain": [0],
"range": ["#4575b4", "#d73027"],
"names": ["below the genome-wide share", "above it"]
},
"legend": { "title": "AluY share" }
}
}
}
]
}
]
}Correlating AluY share with Alu density across the genome
alu_young_share.py prints the Spearman rank correlation of each share against
copies per megabase:
| share, per megabase | bins | Spearman rho | p |
|---|---|---|---|
| AluY share | 2873 | -0.688 | < 1e-300 |
| plus-strand share | 2873 | 0.003 | 0.87 |
The young share falls as the copies rise, and the plus-strand control shows no trend.1
Reproduce it end to end
build_alu_age.sh
runs every step above:
curl -fO https://raw.githubusercontent.com/GMOD/jbrowse-components/main/scripts/build_alu_age.sh
bash build_alu_age.sh # builds ./alu_age_build/jbrowse2
npx --yes serve alu_age_build/jbrowse2 # then open the printed URLBy default the script builds the tracks over UCSC's table.
bash build_alu_age.sh rmsk.bed.gz genome.fa uses your own RepeatMasker BED,
and FAMILY and YOUNG pick another family and its youngest lineage.
See also
- Tutorial: Gene density and transposon density along a chromosome
- Tutorial: RepeatMasker as one row per class
- Tutorial: A grammar of graphics over a BAM (NA12878 insert size)
- Config guide: Mark display
- Docs: JBrowse web quick start
Citations
- Batzer and Deininger (2002). Alu repeats and human genomic diversity, the subfamily lineages and their ages.
- Lander et al. (2001). Initial sequencing and analysis of the human genome, where the distribution of young against old Alu copies along the genome was first described.
- Smit, Hubley and Green (2013-2015). RepeatMasker Open-4.0
Notes
-
Insertions cluster, so the copies per megabase scatter several times more widely than independent copies would. A per-megabase test that assumed independence would flag most of the genome, so the page reports the genome-wide correlation. ↩
Feedback on this tutorial is welcome: contact us.