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.13. 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 it per
copy, count copies per bin zoomed out, and ask where the young copies sit along
chromosome 1. LinearMarkDisplay 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
Where the data comes from
UCSC's hg38 RepeatMasker table, rehosted by genomes.jbrowse.org's hg38 with the Alu rows cut out.
The build script takes these files from their URLs, so there is nothing to download by hand.
- RepeatMasker, UCSC's
rmsktable as BED with a column header: jbrowse.org/…/rmsk.bed.gzhttps://jbrowse.org/ucsc/hg38/rmsk.bed.gz - reference lengths, for the bigWig header: hgdownload.soe.ucsc.edu/…/hg38.chrom.sizeshttps://hgdownload.soe.ucsc.edu/goldenPath/hg38/bigZips/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 a domain to a 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 categoricaldomainfixes 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": "categorical",
"domain": ["AluJ", "AluS", "AluY", "FLAM", "FRAM"],
"range": ["#4575b4", "#fdae61", "#d73027", "#8c8c8c", "#8c8c8c"],
"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": "categorical",
"domain": ["AluJ", "AluS", "AluY", "FLAM", "FRAM"],
"range": ["#4575b4", "#fdae61", "#d73027", "#8c8c8c", "#8c8c8c"],
"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": "categorical",
"domain": ["AluJ", "AluS", "AluY", "FLAM", "FRAM"],
"range": ["#4575b4", "#fdae61", "#d73027", "#8c8c8c", "#8c8c8c"],
"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 ↗
Open this track in JBrowse Desktop ↗ (JBrowse Desktop 5.0+)
Open 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 ↗
Open this track in JBrowse Desktop ↗ (JBrowse Desktop 5.0+)
AluY 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",
"scales": {
"y": {
"domainMin": -1.5,
"domainMax": 1.5,
"title": "AluY share, log2"
}
},
"marks": [
{
"mark": "bar",
"encoding": {
"y": "youngLog2",
"color": {
"field": "youngLog2",
"scale": "threshold",
"domain": [0],
"range": ["#4575b4", "#d73027"],
"labels": ["below the genome-wide share", "above it"],
"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",
"scales": {
"y": {
"domainMin": -1.5,
"domainMax": 1.5,
"title": "AluY share, log2"
}
},
"marks": [
{
"mark": "bar",
"encoding": {
"y": "youngLog2",
"color": {
"field": "youngLog2",
"scale": "threshold",
"domain": [0],
"range": ["#4575b4", "#d73027"],
"labels": ["below the genome-wide share", "above it"],
"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",
"scales": {
"y": {
"domainMin": -1.5,
"domainMax": 1.5,
"title": "AluY share, log2"
}
},
"marks": [
{
"mark": "bar",
"encoding": {
"y": "youngLog2",
"color": {
"field": "youngLog2",
"scale": "threshold",
"domain": [0],
"range": ["#4575b4", "#d73027"],
"labels": ["below the genome-wide share", "above it"],
"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
Checking the bars against the BED file
Read chr1:151,000,000-151,030,000 out of the BED, one line per lineage:
tabix https://jbrowse.org/demos/gene_density/Alu.bed.gz chr1:151,000,000-151,030,000 |
awk -F'\t' '{ l = substr($4, 1, 4); n[l]++; s[l] += $12 }
END { for (l in n) printf "%s\t%d copies\tmean milliDiv %.0f\n", l, n[l], s[l] / n[l] }'| lineage | copies | mean milliDiv |
|---|---|---|
| AluY | 3 | 57 |
| AluS | 30 | 100 |
| FLAM | 4 | 140 |
| AluJ | 12 | 150 |
Count the copies in one red and one blue megabase of the share track:
tabix https://jbrowse.org/demos/gene_density/Alu.bed.gz chr1:191,000,001-192,000,000 |
awk -F'\t' '$2 >= 191000000 && $4 ~ /^Alu[JSY]/ { n++; y += ($4 ~ /^AluY/) }
END { print n " copies, " y " AluY" }'| megabase, chr1 | Alu copies | AluY |
|---|---|---|
| 191 to 192 Mb | 161 | 55 |
| 203 to 204 Mb | 690 | 47 |
The AluY count barely moves between the two megabases, and the older copies pile up in the dense one, so the young share is lower there and its bar is blue.
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 MA, Deininger PL. Alu repeats and human genomic diversity. Nature Reviews Genetics 3:370-379 (2002), 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.