GWAS track
TL;DR: a GWASTrack renders association results as a Manhattan plot. The
main work is prep: a bgzipped, tabix-indexed BED-like file whose score column is
in -log₁₀(p) units (or set scoreTransform to convert). Add a PLINK .ld file
and colorBy: "ld" to color points by linkage disequilibrium to an index SNP.
Preparing the GWAS file
GWASAdapter reads a bgzipped, tabix-indexed BED-like file with a #-prefixed
header row whose score column is in -log₁₀(p) units.
For a file storing raw p-values, set the adapter's
scoreTransform slot to
negLog10 (raw p-value) or negLog10FromLn (natural-log p-value, e.g. a
Pan-UKBB ln P column) and the conversion happens at read time.
Prefix the header so tabix skips it, sort the data rows by chromosome and position, and bgzip/tabix:
# `jbrowse sort-bed` is `sort -k1,1 -k2,2n` with every #-prefixed line kept on
# top rather than sorted into the data, and pins LC_ALL=C so the grouping tabix
# indexes on comes out the same everywhere
sed '1s/^/#/' results.tsv | jbrowse sort-bed | bgzip > results.sorted.txt.gz
tabix -p bed results.sorted.txt.gz
The name column (4th BED field) is the SNP identifier used for LD lookups. If
it is absent, lookups fall back to chr:bp (1-based).
Example file:
#chrom chromStart chromEnd name neg_log_pvalue
chr1 109817590 109817591 rs4970383 1.234
chr1 110162459 110162460 rs4971059 7.891
Preparing the LD file
LD data must be in PLINK --r2 output format. Generate it from a PLINK binary
fileset (.bed/.bim/.fam) or a VCF:
# From a PLINK binary fileset. "dprime" adds the D' column (DP)
plink --bfile study --r2 dprime with-freqs \
--ld-window 99999 --ld-window-kb 1000 --ld-window-r2 0 \
--out study
# Or starting from a VCF
plink --vcf study.vcf.gz --r2 dprime with-freqs \
--ld-window 99999 --ld-window-kb 1000 --ld-window-r2 0 \
--out study
This writes study.ld with columns CHR_A BP_A SNP_A CHR_B BP_B SNP_B R2 (plus
DP for D' and MAF_A/MAF_B from the dprime/with-freqs flags).
--ld-window-r2 0 keeps every pair (PLINK otherwise drops pairs below r²=0.2),
and the --ld-window* flags raise the default limits on how far apart paired
SNPs may be. Tune them to the span you want rendered.
dprime also switches r² itself to the haplotype-frequency estimate, so the R2
column of a run with it and a run without it are two different statistics. This
is PLINK 1.9; plink2 replaced --r2 with --r2-phased and --r2-unphased.
For regional analyses the plain study.ld file works as-is with
PlinkLDAdapter. For chromosome-scale or
genome-wide LD, bgzip and tabix the file so only pairs in the visible region are
fetched, then use PlinkLDTabixAdapter:
# plink pads its columns with spaces and tabix indexes on tabs, so the awk
# retabs as well as commenting the header. `jbrowse sort-bed` is then
# `sort -k1,1 -k2,2n` under LC_ALL=C with that `#` line kept on top.
awk 'NR == 1 {$1 = "#"$1} {$1 = $1}1' OFS='\t' study.ld |
jbrowse sort-bed | bgzip > study.sorted.ld.gz
# BP positions in PLINK output are 1-based, matching tabix's default
tabix -s 1 -b 2 -e 2 study.sorted.ld.gz
The # is worth the extra step. tabix keeps a header two ways: commented with
the meta character, or counted with -S 1. Only the commented form is what
tabix -H prints and what most readers, JBrowse included, ask for first, so a
-S 1 file's header is easy for a tool to miss entirely — which costs you the
DP column, and with it the option of drawing D' rather than r². Marking the
header with -c C makes the meta character C, so every chr1-style data row
reads as a comment.
A file already indexed with -S 1 still loads; JBrowse reads the header either
way.
GWASAdapter
GWASAdapter reads the bgzipped results from bedGzLocation with a tabix
index.location, or from the
uri shorthand, which
resolves the sibling <file>.tbi. scoreColumn names the header column plotted
on the Y axis. See the autogenerated GWASAdapter docs for
defaults and the full slot list.
The bgzipped output above ends in .txt.gz, which auto-detects as GWASAdapter
in the Add Track dialog. Other extensions such as .bed.gz require manual
adapter selection.
LinearManhattanDisplay
On the display:
colorsets the point color, a CSS literal or ajexl:expressioncolorBy: "ld"colors points by r² to the index SNP, and requires anldAdaptersub-adapter on theGWASAdapterscatterPointSizesets the point diameter in px
See the autogenerated LinearManhattanDisplay docs for defaults and the full slot list.
Examples
A minimal track, using the
uri shorthand for the
tabix index:
{
"type": "GWASTrack",
"trackId": "my_gwas",
"name": "My GWAS",
"assemblyNames": ["hg38"],
"adapter": {
"type": "GWASAdapter",
"uri": "https://yourhost/results.bed.gz"
}
}
jbrowse add-track https://yourhost/results.bed.gz \
--trackId my_gwas \
--name "My GWAS" \
--assemblyNames hg38 \
--adapterType GWASAdapter \
--trackType GWASTrack
Coloring points by LD to the index SNP. Swap PlinkLDAdapter for
PlinkLDTabixAdapter when the .ld file is bgzipped and tabix-indexed:
{
"type": "GWASTrack",
"trackId": "sle_gwas",
"name": "SLE GWAS",
"assemblyNames": ["hg19"],
"adapter": {
"type": "GWASAdapter",
"uri": "https://yourhost/sle.bed.gz",
"scoreColumn": "neg_log_pvalue",
"ldAdapter": {
"type": "PlinkLDAdapter",
"uri": "https://yourhost/sle.ld"
}
},
"displayDefaults": {
"colorBy": "ld"
}
}
For a per-feature color, set color to a jexl expression such as
"jexl:feature.score > 7.3 ? 'red' : '#0068d1'". See the
jexl guide, and
property access vs get()
for when to use the get(feature,'score') form.