QTL mapping (BXD mice)
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.
The BXD mice are about 200 recombinant-inbred strains bred down from two parents, C57BL/6J (B6, the "B" parent) and DBA/2J (the "D" parent), so each strain is a mosaic of blocks from one or the other. We paint each strain by which parent gave it each block and stack the painting under a GeneNetwork QTL scan of coat color (a QTL is a region linked to variation in a trait). Banding the strains by coat color then shows which blocks under each peak set it.
Prerequisites
curl, to fetch GeneNetwork's QTL scanjq, to reshape itpython3, for the painting- htslib (
bgzip,tabix), for the painting - A JBrowse instance to add the tracks to (web quickstart or desktop quickstart)
On Debian/Ubuntu, apt install curl jq python3 tabix covers it.
Where the data comes from
BXD consensus genotypes and QTL scans from GeneNetwork (Wang et al. 2016).
The build script fetches these files, so there is nothing to download by hand.
- the BXD consensus genotypes, 198 strains: gn1.genenetwork.org/…/BXD.genohttps://gn1.genenetwork.org/genotypes/BXD.geno
- the coat-color scan (trait 11280), from GeneNetwork's mapping API: genenetwork.org/…/mappinghttps://genenetwork.org/api/v_pre1/mapping?db=BXDPublish&method=gemma&trait_id=11280
- the coat-color scores per strain, from GeneNetwork's sample-data API: genenetwork.org/…/11280https://genenetwork.org/api/v_pre1/sample_data/BXDPublish/11280
Reading GeneNetwork's BXD genotype file
GeneNetwork distributes the consensus BXD genotypes as a plain-text .geno
file. Each row is a marker with a cM genetic-map position and an mm10 Mb
physical position, and the painting uses Mb. Each column is a strain, with a
one-letter genotype, B, D, H (heterozygous) or U (unknown):
@name:BXD
@mat:B
@pat:D
Chr Locus cM Mb BXD1 BXD2 BXD5 ...
1 rs31443144 0.11 3.010274 B B D ...
1 rs6269442 0.21 3.492195 B B D ...The painting skips the F1 columns, since an F1 (a B by D hybrid) is heterozygous at every marker and GeneNetwork computes the scan over the strains.
Painting each strain's B and D blocks
The painting draws one row per strain, each block colored by genotype.
bxd_geno_to_painting_bed.py
reads the markers of each strain in order along every chromosome and writes one
BED interval per run of consecutive same-genotype markers (run-length encoding),
coloring B/D/H and writing the strain name into an extra sample column:
#chrom chromStart chromEnd name score strand thickStart thickEnd itemRgb sample genotype
chr1 3001490 20291558 B 0 . 3001490 20291558 65,105,225 BXD1 B
chr1 20291558 53451539 D 0 . 20291558 53451539 220,60,50 BXD1 D
chr1 53451539 69355875 B 0 . 53451539 69355875 65,105,225 BXD1 BRun it on the .geno, then sort, bgzip and tabix the result:
curl -fO https://raw.githubusercontent.com/GMOD/jbrowse-components/main/scripts/bxd_geno_to_painting_bed.py
python3 bxd_geno_to_painting_bed.py BXD.geno bxd_painting.bed
jbrowse sort-bed bxd_painting.bed | bgzip > bxd_painting.bed.gz
tabix -p bed bxd_painting.bed.gzsort-bed keeps the # header on top and sorts
the rest under LC_ALL=C, so the adapter reads the column names off the file
whatever your locale.
Both tracks use the mm10 assembly, whose chromosome names the marker positions
follow:
Goes in the assemblies array of config.json. See Assemblies.
{
"name": "mm10",
"aliases": ["GRCm38"],
"uri": "https://jbrowse.org/genomes/mm10/fasta/mm10.fa.gz",
"refNameAliases": {
"uri": "https://hgdownload.soe.ucsc.edu/goldenpath/mm10/bigZips/latest/mm10.chromAlias.txt"
},
"cytobands": "https://jbrowse.org/ucsc/mm10/cytoBandIdeo.bed.gz"
}jbrowse add-assembly https://jbrowse.org/genomes/mm10/fasta/mm10.fa.gz \
--name mm10 \
--alias GRCm38 \
--refNameAliases https://hgdownload.soe.ucsc.edu/goldenpath/mm10/bigZips/latest/mm10.chromAlias.txt \
--config '{"cytobands":"https://jbrowse.org/ucsc/mm10/cytoBandIdeo.bed.gz"}'The painting track is a FeatureTrack with a
LinearMultiRowFeatureDisplay. In
grammar-of-graphics terms, rows assigns one row per sample and color is a
color scale. For your own panel, swap uri for the bgzipped, tabix-indexed BED
your conversion wrote.
Goes in the tracks array of config.json. See Tracks.
{
"type": "FeatureTrack",
"trackId": "bxd_chromosome_painting_mm10",
"name": "BXD chromosome painting (GeneNetwork, 198 strains)",
"assemblyNames": ["mm10"],
"adapter": {
"type": "BedTabixAdapter",
"disableGeneHeuristic": true,
"uri": "https://jbrowse.org/demos/bxd/bxd_painting.bed.gz"
},
"displays": [
{
"type": "LinearMultiRowFeatureDisplay",
"rows": "sample",
"color": {
"scale": "identity",
"domain": ["rgb(65,105,225)", "rgb(220,60,50)", "rgb(150,150,150)"],
"labels": ["B (C57BL/6J)", "D (DBA/2J)", "H (heterozygous)"],
"title": "Genotype"
}
}
]
}jbrowse add-track-json '{
"type": "FeatureTrack",
"trackId": "bxd_chromosome_painting_mm10",
"name": "BXD chromosome painting (GeneNetwork, 198 strains)",
"assemblyNames": ["mm10"],
"adapter": {
"type": "BedTabixAdapter",
"disableGeneHeuristic": true,
"uri": "https://jbrowse.org/demos/bxd/bxd_painting.bed.gz"
},
"displays": [
{
"type": "LinearMultiRowFeatureDisplay",
"rows": "sample",
"color": {
"scale": "identity",
"domain": ["rgb(65,105,225)", "rgb(220,60,50)", "rgb(150,150,150)"],
"labels": ["B (C57BL/6J)", "D (DBA/2J)", "H (heterozygous)"],
"title": "Genotype"
}
}
]
}'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": "bxd_chromosome_painting_mm10",
"name": "BXD chromosome painting (GeneNetwork, 198 strains)",
"assemblyNames": ["mm10"],
"adapter": {
"type": "BedTabixAdapter",
"disableGeneHeuristic": true,
"uri": "https://jbrowse.org/demos/bxd/bxd_painting.bed.gz"
},
"displays": [
{
"type": "LinearMultiRowFeatureDisplay",
"rows": "sample",
"color": {
"scale": "identity",
"domain": ["rgb(65,105,225)", "rgb(220,60,50)", "rgb(150,150,150)"],
"labels": ["B (C57BL/6J)", "D (DBA/2J)", "H (heterozygous)"],
"title": "Genotype"
}
}
]
}Open this track in JBrowse Web ↗
Open this track in JBrowse Desktop ↗ (JBrowse Desktop 5.0+)
scale: "identity"on thecolorkeeps theitemRgbcolors, and itslabelsname the genotype eachdomaincolor stands for. The same entries fill the Categories toggles in the track menu, so hidingHisolates the B/D contrast.disableGeneHeuristic: truestops the BED adapter from reading each block as a gene, which thethickStart/thickEndcolumns would otherwise trigger.
Loading the coat-color QTL scan as a Manhattan track
GeneNetwork maps these traits itself, and its API serves the per-marker result
of a GEMMA run. We fetch the scan for one trait by its GeneNetwork id and
reshape it with jq into records of a marker, its mm10 position in Mb, a LOD
score (log odds of linkage) and a p-value:
GN='https://genenetwork.org/api/v_pre1/mapping?db=BXDPublish&method=gemma'
curl -fsSL "$GN&trait_id=11280" -o coat_color.json # 11280 = coat color
{
printf '#chrom\tstart\tend\tname\tscore\tstrand\tlod\n'
jq -r '.[0][] | [ "chr" + (.chr|tostring), ((.Mb*1000000)|round),
((.Mb*1000000)|round + 1), .name, ".", ".",
(.lod_score*10000|round/10000) ] | @tsv' coat_color.json |
sort -k1,1 -k2,2n
} | bgzip > bxd_gwas_coatcolor.tsv.gz
tabix -p bed bxd_gwas_coatcolor.tsv.gzThe commands write a tabix-indexed BED-like table, one line per marker:
#chrom start end name score strand lod
chr4 81304223 81304224 rs3708061 . . 48.1126The trait id is the number in the GeneNetwork URL, and
bxd_phenocovar.csv in the
qtl2data BXD release lists the ids with their descriptions.
A GWASTrack with a GWASAdapter reads one column of that file as the
Manhattan score (GWAS track guide):
scoreColumnnames the column. The default is a pre-computed-log10(p)calledneg_log_pvalue, so a LOD column needs it set.scoreTransformstays at its default, since a LOD is already on the scale the plot draws. A raw or natural-log p-value column would need it.
Goes in the tracks array of config.json. See Tracks.
{
"type": "GWASTrack",
"trackId": "bxd_gwas_coatcolor_mm10",
"name": "BXD QTL: coat color (GEMMA)",
"assemblyNames": ["mm10"],
"adapter": {
"type": "GWASAdapter",
"uri": "bxd_gwas_coatcolor.tsv.gz",
"scoreColumn": "lod"
},
"displayDefaults": {
"scales": { "y": { "title": "LOD" } }
}
}jbrowse add-track-json '{
"type": "GWASTrack",
"trackId": "bxd_gwas_coatcolor_mm10",
"name": "BXD QTL: coat color (GEMMA)",
"assemblyNames": ["mm10"],
"adapter": {
"type": "GWASAdapter",
"uri": "bxd_gwas_coatcolor.tsv.gz",
"scoreColumn": "lod"
},
"displayDefaults": {
"scales": { "y": { "title": "LOD" } }
}
}'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": "GWASTrack",
"trackId": "bxd_gwas_coatcolor_mm10",
"name": "BXD QTL: coat color (GEMMA)",
"assemblyNames": ["mm10"],
"adapter": {
"type": "GWASAdapter",
"uri": "bxd_gwas_coatcolor.tsv.gz",
"scoreColumn": "lod"
},
"displayDefaults": {
"scales": { "y": { "title": "LOD" } }
}
}bxd_gwas_coatcolor.tsv.gz is relative to a config.json. Replace it with its URL or its path on this computer.
Faceting the strains by coat color
The scan has a plateau of tied markers on chr4, whose interval contains Tyrp1, the brown coat-color gene, and a lower peak on chr9. GeneNetwork scores coat color on a four-step scale: black is 4, grey 3, brown 2 and DBA/2's dilute brown 1. We'll fetch each strain's score and turn the four values into four sections of painting rows, so the phenotype orders the rows and the painted genotypes can agree with it or not.
The scores come from GeneNetwork's sample-data API. jq turns them into one
rowGroups entry
per score, a regex matching that score's strains, and a
rowColor that
pairs each group with its coat color:
curl -fsSL https://genenetwork.org/api/v_pre1/sample_data/BXDPublish/11280 -o coat_color_values.json
jq '{ "4": ["black", "rgb(30,30,30)"], "3": ["grey", "rgb(150,150,160)"],
"2": ["brown", "rgb(130,80,40)"], "1": ["dilute brown", "rgb(210,175,130)"] } as $class
| [ .[] | select((.sample_name | startswith("BXD")) and (.value | IN(1, 2, 3, 4))) ]
| group_by(-.value)
| map($class[.[0].value | floor | tostring] + [map(.sample_name) | join("|")])
| { rowGroups: map({ match: ("^(" + .[2] + ")$"), group: .[0] }),
rowColor: { field: "group", domain: map(.[0]), range: map(.[1]) } }' \
coat_color_values.json > coatColor.jsonAdd both keys to the painting's display entry, beside rows and color,
together with facet: "group":
rowGroupstags each strain with its coat color as itsgroup.facet: "group"splits the display into one section pergroup, black at the top. The few strains scored between two steps match no entry and form a section at the bottom.rowColorbygroupis a color scale on each row's label bar, showing the strain's coat color. The blocks keep their genotype colors, and the unscored strains, with no group, get no color.
The fence below holds those new keys, coatColor.json with facet added.
https://jbrowse.org/code/jb2/main/test_data/config_bxd.json holds the whole
track, as bxd_chromosome_painting_mm10.
Advanced → Edit plot... in the track menu shows these keys as text and applies edits live.
The generated coatColor.json keys
{
"rowGroups": [
{
"match": "^(BXD100|BXD105|BXD109|BXD11|BXD110|BXD116|BXD119|BXD120|BXD121|BXD123|BXD124|BXD125|BXD128a|BXD131|BXD133|BXD136|BXD14|BXD142|BXD145|BXD148|BXD149|BXD151|BXD152|BXD153|BXD154|BXD156|BXD165|BXD171|BXD173|BXD186|BXD190|BXD191|BXD199|BXD2|BXD20|BXD204|BXD207|BXD210|BXD217|BXD218|BXD219|BXD23|BXD31|BXD32|BXD34|BXD35|BXD42|BXD43|BXD48|BXD48a|BXD50|BXD51|BXD56|BXD86|BXD87)$",
"group": "black"
},
{
"match": "^(BXD101|BXD117|BXD12|BXD122|BXD130|BXD135|BXD139|BXD141|BXD144|BXD146|BXD147|BXD155|BXD157|BXD16|BXD162|BXD169|BXD172|BXD174|BXD175|BXD176|BXD178|BXD18|BXD180|BXD181|BXD183|BXD184|BXD19|BXD198|BXD202|BXD205|BXD208|BXD211|BXD212|BXD215|BXD22|BXD29|BXD33|BXD38|BXD39|BXD40|BXD49|BXD5|BXD6|BXD76|BXD79|BXD8|BXD94)$",
"group": "grey"
},
{
"match": "^(BXD102|BXD104|BXD106|BXD108|BXD111|BXD114|BXD115|BXD126|BXD127|BXD128|BXD13|BXD132|BXD134|BXD15|BXD150|BXD177|BXD192|BXD193|BXD194|BXD195|BXD196|BXD197|BXD200|BXD203|BXD209|BXD24|BXD24a|BXD25|BXD27|BXD28|BXD36|BXD52|BXD53|BXD55|BXD59|BXD60|BXD62|BXD65|BXD65a|BXD66|BXD68|BXD72|BXD74|BXD78|BXD88)$",
"group": "brown"
},
{
"match": "^(BXD1|BXD107|BXD112|BXD113|BXD138|BXD160|BXD161|BXD168|BXD170|BXD187|BXD188|BXD189|BXD201|BXD206|BXD21|BXD216|BXD220|BXD30|BXD44|BXD45|BXD61|BXD63|BXD64|BXD65b|BXD67|BXD69|BXD70|BXD71|BXD73|BXD73a|BXD73b|BXD75|BXD77|BXD81|BXD83|BXD84|BXD85|BXD89|BXD9|BXD90|BXD91|BXD93|BXD95|BXD98|BXD99)$",
"group": "dilute brown"
}
],
"facet": "group",
"rowColor": {
"field": "group",
"domain": ["black", "grey", "brown", "dilute brown"],
"range": [
"rgb(30,30,30)",
"rgb(150,150,160)",
"rgb(130,80,40)",
"rgb(210,175,130)"
]
}
}Add the mm10 RefSeq genes
(https://jbrowse.org/demos/bxd/mm10.ncbiRefSeq.sorted.gtf.gz, with its .tbi
beside it), then:
- Filter them to Tyrp1 with Filter by... in their track menu
(
jexl:feature.name=='Tyrp1'). - Open chr4 with the genes, the scan and the painting.
Under the peak, black and grey strains have B6's copy of Tyrp1 and brown and dilute brown strains DBA/2's; the rest of chr4 is the control, where nothing sets coat color.
Myo5a, the dilute locus, under the chr9 peak
The chr9 peak falls on Myo5a, the dilute locus (its mutation lightens coat color). The same grouped painting, with the gene track filtered to Myo5a, splits the sections the other way:
Reproduce it end to end
bxd_build_demo.sh
runs every step above. It:
- downloads JBrowse and the GeneNetwork consensus genotypes
- builds the painting with
bxd_geno_to_painting_bed.py - fetches the coat-color scan (trait
11280) and the per-strain scores from GeneNetwork - writes a
config.jsonthat opens on mm10 chr4 with the scan over the painting banded by coat color, and prints the marker at the peak and its LOD as it goes
curl -fO https://raw.githubusercontent.com/GMOD/jbrowse-components/main/scripts/bxd_build_demo.sh
bash bxd_build_demo.sh # builds ./bxd_demo/jbrowse2
npx --yes serve bxd_demo/jbrowse2 # then open the printed URLAny GeneNetwork trait id works in place of 11280.
See also
- Tutorial: ChromHMM chromatin states
- Tutorial: Phased trio analysis (1000 Genomes)
- Tutorial: Selection scans (Drosophila DGRP)
- Tutorial: A selected haplotype (Dog10K)
- Tutorial: Local ancestry (Dog10K)
- User guide: GWAS / Manhattan track
- User guide: Clustering rows
- Config guide: GWAS track
- Config guide: Using jexl callbacks
Feedback on this tutorial is welcome: contact us.