Pangenome (pggb)
3 videos in this tutorial
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.20. Desktop beta builds for Linux and
macOS are on the releases page;
the Windows build waits on code signing.
A pangenome graph stores the sequence several genomes share once, as a path every genome follows, and branches where they differ. We build one of five E. coli strains with pggb and turn its outputs into tracks on the K12 genome. Most are linear projections, which flatten the graph onto K12's coordinates so it loads as an ordinary track; together they show which sequence all five strains share and which only some have. We:
- load the alignment between the strains as a synteny track, and stack the strains in a linear synteny view
- load the variants the graph calls against K12 as a variant track, one row per strain
- load the graph's whole-genome alignment as a MAF track on K12
- plot depth as a quantitative track and per-strain presence as a heatmap
- load the graph itself as a graph track you browse by locus
The graph view is a beta plugin. We welcome your feedback.
Prerequisites
dockerorsingularity, for the pggb imagesamtools, htslib (bgzip,tabix),python3andunzipbedGraphToBigWig(UCSC kentUtils)minimap2, for the reads that check the depth tracknode, for the JBrowse CLI- the NCBI
datasetsCLI gfa-to-tabix2.4.0 or later, for the graph track- the GraphGenomeView plugin, for the same track. In your own JBrowse, install it from the plugin store
# Debian/Ubuntu
sudo apt install samtools tabix minimap2 nodejs
# or conda, which has every tool above
conda install -c bioconda -c conda-forge samtools htslib minimap2 nodejs \
ucsc-bedgraphtobigwig ncbi-datasets-cli
# 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 \
https://ftp.ncbi.nlm.nih.gov/pub/datasets/command-line/v2/linux-amd64/datasets \
https://ftp.ncbi.nlm.nih.gov/pub/datasets/command-line/v2/linux-amd64/dataformat
chmod +x ~/.local/bin/bedGraphToBigWig ~/.local/bin/datasets ~/.local/bin/dataformat
# any platform
cargo install gfa-to-tabix
npm install -g @jbrowse/cli@nextThe data
The page's commands fetch the five E. coli RefSeq assemblies by accession and build local files from them; run the build script first, or follow the same commands on your own assemblies. The graph tracks read our hosted copy, and the nanopore reads under Testing the CPZ-55 depth trough are the one file the script does not fetch.
| What it is | File |
|---|---|
| K12 | GCF_000005845.2_ASM584v2/ |
| Sakai | GCF_000008865.2_ASM886v2/ |
| CFT073 | GCF_000007445.1_ASM744v1/ |
| NCTC86 | GCF_002007705.1_ASM200770v1/ |
| IAI39 | GCF_000026345.1_ASM2634v1/ |
| Nanopore reads from an unrelated isolate, E. coli E146 | DRR193901_1.fastq.gz |
Building the graph
Each strain's FASTA holds its chromosome as one record named chr. Fetch and
trim one assembly with the NCBI datasets CLI:
# $acc: the RefSeq accession; $strain: the name the graph gives the sample
datasets download genome accession "$acc" --filename "$strain.zip"
unzip -o "$strain.zip" -d "$strain"
# keep the first record, the chromosome, renamed chr; the rest are plasmids
awk '/^>/{n++; if (n == 1) print ">chr"; next} n == 1' \
"$strain"/ncbi_dataset/data/*/*.fna > "$strain.fa"pggb takes one FASTA of all the genomes, with
PanSN names,
sample#haplotype#contig. Concatenate the five haploid strains as haplotype 1
and index the result:
for strain in K12 Sakai CFT073 NCTC86 IAI39; do
awk -v s="$strain" '/^>/{print ">" s "#1#chr"; next} {print}' "$strain.fa"
done > all.fa
bgzip all.fa
samtools faidx all.fa.gzThe pggb image includes every tool below,
odgi among them, so wrap docker run once
and run pggb:
in_pggb() {
docker run --rm -u "$(id -u):$(id -g)" -w /data -v "$PWD":/data \
ghcr.io/pangenome/pggb:202603141454453ade6b "$@"
}
# -n: haplotype count
# -c: mappings kept per segment; raise it along with -n
# -p: minimum identity; -s: segment length
# -V K12:10000: a VCF against the K12 path; -M: a MAF
in_pggb pggb -i /data/all.fa.gz -o /data/pggb \
-n 5 -c 4 -p 90 -s 5000 -V K12:10000 -M -t "$(getconf _NPROCESSORS_ONLN)"
og=$(ls pggb/*.smooth.final.og)Under singularity,
singularity exec --bind "$PWD":/data --pwd /data docker://<image> replaces the
wrapper body.
Load the genomes
Every projection is a track on a strain's assembly, and each strain's FASTA
holds one sequence named chr, the refName the graph's paths are renamed to
below. We'll add K12, the reference the projections target, from a
bgzip-compressed, indexed FASTA:
Goes in the assemblies array of config.json. See Assemblies.
{
"name": "K12",
"uri": "https://jbrowse.org/demos/ecoli_pangenome/K12.fa.gz"
}jbrowse add-assembly https://jbrowse.org/demos/ecoli_pangenome/K12.fa.gz \
--name K12In 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/demos/ecoli_pangenome/K12.fa.gz
https://jbrowse.org/demos/ecoli_pangenome/K12.fa.gz.fai
https://jbrowse.org/demos/ecoli_pangenome/K12.fa.gz.gziJBrowse reads the format off the file name. Then fill in:
- Genome name:
K12
Sakai, CFT073, NCTC86 and IAI39 load the same way, named as in the
assemblyNames of the tracks below.
Synteny between the strains (wfmash)
pggb's first step is an all-vs-all alignment by wfmash, which JBrowse reads as a
synteny track once jbrowse make-pif indexes it:
cp pggb/*.alignments.wfmash.paf ecoli_pggb_ava.paf
jbrowse make-pif ecoli_pggb_ava.paf --csiGoes in the tracks array of config.json. See Tracks.
{
"type": "SyntenyTrack",
"trackId": "ecoli_pggb_ava",
"name": "pggb graph: all-vs-all synteny (wfmash)",
"assemblyNames": ["K12", "Sakai", "CFT073", "NCTC86", "IAI39"],
"adapter": {
"type": "MultiGenomeIndexedPAFAdapter",
"uri": "ecoli_pggb_ava.pif.gz",
"csi": true,
"assemblyNames": ["K12", "Sakai", "CFT073", "NCTC86", "IAI39"]
}
}jbrowse add-track-json '{
"type": "SyntenyTrack",
"trackId": "ecoli_pggb_ava",
"name": "pggb graph: all-vs-all synteny (wfmash)",
"assemblyNames": ["K12", "Sakai", "CFT073", "NCTC86", "IAI39"],
"adapter": {
"type": "MultiGenomeIndexedPAFAdapter",
"uri": "ecoli_pggb_ava.pif.gz",
"csi": true,
"assemblyNames": ["K12", "Sakai", "CFT073", "NCTC86", "IAI39"]
}
}'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": "SyntenyTrack",
"trackId": "ecoli_pggb_ava",
"name": "pggb graph: all-vs-all synteny (wfmash)",
"assemblyNames": ["K12", "Sakai", "CFT073", "NCTC86", "IAI39"],
"adapter": {
"type": "MultiGenomeIndexedPAFAdapter",
"uri": "ecoli_pggb_ava.pif.gz",
"csi": true,
"assemblyNames": ["K12", "Sakai", "CFT073", "NCTC86", "IAI39"]
}
}ecoli_pggb_ava.pif.gz is relative to a config.json. Replace it with its URL or its path on this computer.
That shows it in the linear view. For the synteny view, open Add → Linear synteny view, pick the track under Quick start, and click Launch.
Stack the strains with Add → Linear synteny view, whose Quick start fills in a row per assembly the track lists.
Select a span on K12's ruler and pick Launch → Linear synteny view to stack the strains over that span alone. The same selection on another strain's row, with Replace current view, re-anchors the stack on that strain:
Synteny read out of the graph with odgi untangle
odgi untangle
follows each strain's path and reports which stretch of the K12 path it
traverses, so this synteny comes from the graph itself. -p writes it as a PAF,
which make-pif indexes the same way:
printf 'K12#1#chr\n' > target.txt
printf 'Sakai#1#chr\nCFT073#1#chr\nNCTC86#1#chr\nIAI39#1#chr\n' > query.txt
# -m merges runs shorter than this into the previous segment
# -j is the minimum jaccard similarity for a match
# -e forces a segment boundary every N bp; omit it on graphs with many haplotypes
in_pggb odgi untangle -i "/data/$og" \
-R /data/target.txt -Q /data/query.txt -m 1000 -j 0.5 -e 5000 -p -t "$(getconf _NPROCESSORS_ONLN)" \
> ecoli_pggb_untangle.paf
jbrowse make-pif ecoli_pggb_untangle.paf --csiGoes in the tracks array of config.json. See Tracks.
{
"type": "SyntenyTrack",
"trackId": "ecoli_pggb_untangle",
"name": "pggb graph: synteny from the graph (odgi untangle)",
"assemblyNames": ["K12", "Sakai", "CFT073", "NCTC86", "IAI39"],
"adapter": {
"type": "MultiGenomeIndexedPAFAdapter",
"uri": "ecoli_pggb_untangle.pif.gz",
"csi": true,
"assemblyNames": ["K12", "Sakai", "CFT073", "NCTC86", "IAI39"]
}
}jbrowse add-track-json '{
"type": "SyntenyTrack",
"trackId": "ecoli_pggb_untangle",
"name": "pggb graph: synteny from the graph (odgi untangle)",
"assemblyNames": ["K12", "Sakai", "CFT073", "NCTC86", "IAI39"],
"adapter": {
"type": "MultiGenomeIndexedPAFAdapter",
"uri": "ecoli_pggb_untangle.pif.gz",
"csi": true,
"assemblyNames": ["K12", "Sakai", "CFT073", "NCTC86", "IAI39"]
}
}'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": "SyntenyTrack",
"trackId": "ecoli_pggb_untangle",
"name": "pggb graph: synteny from the graph (odgi untangle)",
"assemblyNames": ["K12", "Sakai", "CFT073", "NCTC86", "IAI39"],
"adapter": {
"type": "MultiGenomeIndexedPAFAdapter",
"uri": "ecoli_pggb_untangle.pif.gz",
"csi": true,
"assemblyNames": ["K12", "Sakai", "CFT073", "NCTC86", "IAI39"]
}
}ecoli_pggb_untangle.pif.gz is relative to a config.json. Replace it with its URL or its path on this computer.
That shows it in the linear view. For the synteny view, open Add → Linear synteny view, pick the track under Quick start, and click Launch.
untangle_to_bed.py turns the same PAF into a BED with a strain column, so
one track can draw every strain on K12's axis. In
grammar-of-graphics terms, rows assigns
one row per strain, and encoding.color is a color scale that here uses
scale.type: "identity" for the colors the file states, red where the strain
runs inverted:
curl -fO https://raw.githubusercontent.com/GMOD/jbrowse-components/main/scripts/untangle_to_bed.py
python3 untangle_to_bed.py ecoli_pggb_untangle.paf chr > ecoli_pggb_untangle_rows.bed
jbrowse sort-bed ecoli_pggb_untangle_rows.bed | bgzip > ecoli_pggb_untangle_rows.bed.gz
tabix -C -p bed ecoli_pggb_untangle_rows.bed.gzGoes in the tracks array of config.json. See Tracks.
{
"type": "FeatureTrack",
"trackId": "ecoli_pggb_untangle_rows",
"name": "pggb graph: untangle per strain (orientation, vs K12)",
"uri": "ecoli_pggb_untangle_rows.bed.gz",
"csi": true,
"assemblyNames": ["K12"],
"displays": [
{
"type": "LinearMultiRowFeatureDisplay",
"rows": {
"field": "strain",
"sort": ["Sakai", "CFT073", "NCTC86", "IAI39"]
},
"encoding": {
"color": {
"scale": {
"type": "identity",
"domain": ["rgb(153,153,153)", "rgb(214,39,40)"],
"names": ["Same orientation as K12", "Inverted"]
}
}
}
}
]
}jbrowse add-track-json '{
"type": "FeatureTrack",
"trackId": "ecoli_pggb_untangle_rows",
"name": "pggb graph: untangle per strain (orientation, vs K12)",
"uri": "ecoli_pggb_untangle_rows.bed.gz",
"csi": true,
"assemblyNames": ["K12"],
"displays": [
{
"type": "LinearMultiRowFeatureDisplay",
"rows": {
"field": "strain",
"sort": ["Sakai", "CFT073", "NCTC86", "IAI39"]
},
"encoding": {
"color": {
"scale": {
"type": "identity",
"domain": ["rgb(153,153,153)", "rgb(214,39,40)"],
"names": ["Same orientation as K12", "Inverted"]
}
}
}
}
]
}'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": "ecoli_pggb_untangle_rows",
"name": "pggb graph: untangle per strain (orientation, vs K12)",
"uri": "ecoli_pggb_untangle_rows.bed.gz",
"csi": true,
"assemblyNames": ["K12"],
"displays": [
{
"type": "LinearMultiRowFeatureDisplay",
"rows": {
"field": "strain",
"sort": ["Sakai", "CFT073", "NCTC86", "IAI39"]
},
"encoding": {
"color": {
"scale": {
"type": "identity",
"domain": ["rgb(153,153,153)", "rgb(214,39,40)"],
"names": ["Same orientation as K12", "Inverted"]
}
}
}
}
]
}ecoli_pggb_untangle_rows.bed.gz is relative to a config.json. Replace it with its URL or its path on this computer.
Variants against K12 (pggb -V)
pggb -V wrote a VCF of every variant the graph decomposes against the K12
path, genotyped in the other four strains. Its CHROM is the PanSN path,
K12#1#chr, so rename it to the assembly's chr:
printf 'K12#1#chr\tchr\n' > rename_chrs.tsv
in_pggb bash -c "bcftools annotate --rename-chrs /data/rename_chrs.tsv \
/data/pggb/*.smooth.final.K12.decomposed.vcf \
| bcftools sort -Oz -o /data/ecoli_pggb.vcf.gz && tabix -C -p vcf /data/ecoli_pggb.vcf.gz"Each strain is one haplotype, so rows: "haplotype" draws one row per strain,
colored by that strain's allele:
Goes in the tracks array of config.json. See Tracks.
{
"type": "VariantTrack",
"trackId": "ecoli_pggb_variants",
"name": "pggb graph: pangenome variants (vs K12)",
"assemblyNames": ["K12"],
"adapter": {
"type": "VcfTabixAdapter",
"uri": "ecoli_pggb.vcf.gz",
"csi": true
},
"displays": [{ "type": "LinearVariantDisplay", "rows": "haplotype" }]
}jbrowse add-track-json '{
"type": "VariantTrack",
"trackId": "ecoli_pggb_variants",
"name": "pggb graph: pangenome variants (vs K12)",
"assemblyNames": ["K12"],
"adapter": {
"type": "VcfTabixAdapter",
"uri": "ecoli_pggb.vcf.gz",
"csi": true
},
"displays": [{ "type": "LinearVariantDisplay", "rows": "haplotype" }]
}'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": "VariantTrack",
"trackId": "ecoli_pggb_variants",
"name": "pggb graph: pangenome variants (vs K12)",
"assemblyNames": ["K12"],
"adapter": {
"type": "VcfTabixAdapter",
"uri": "ecoli_pggb.vcf.gz",
"csi": true
},
"displays": [{ "type": "LinearVariantDisplay", "rows": "haplotype" }]
}ecoli_pggb.vcf.gz is relative to a config.json. Replace it with its URL or its path on this computer.
With a length after the path name, as in -V K12:10000, pggb runs
vcfbub and
vcfwave over the VCF to decompose large
sites into smaller variants, giving *.decomposed.vcf.
The undecomposed *.smooth.final.K12.vcf keeps one record per bubble (where
paths split and rejoin) for the
graph track to read.
Whole-genome alignment onto K12 (MAF)
pggb -M wrote the multiple alignment as a MAF. Re-root every block on K12,
rename the rows to sample.chr, and blank the padding smoothxg writes around
each row, then convert the MAF to the CSI-indexed BED that
MafTabixAdapter reads:
curl -fO https://raw.githubusercontent.com/GMOD/jbrowse-components/main/scripts/reroot_maf.py
# all.fa.gz is the pggb input: the padding sits on either end of a row, so
# only the genome says which bases are the row's own
python3 reroot_maf.py pggb/*.smooth.maf ecoli_pggb.maf all.fa.gz K12#1#chrcurl -fO https://raw.githubusercontent.com/GMOD/jbrowse-components/main/scripts/maf_to_bed.py
python3 maf_to_bed.py ecoli_pggb.maf ecoli_pggb.maf.bed
bgzip ecoli_pggb.maf.bed
tabix -C -p bed ecoli_pggb.maf.bed.gzTo order the rows by how much graph each pair of strains shares, build a tree
from
odgi similarity
and point nhLocation at it:
curl -fO https://raw.githubusercontent.com/GMOD/jbrowse-components/main/scripts/odgi_similarity_to_newick.py
in_pggb odgi similarity -i "/data/$og" -D '#' -p 1 > ecoli_pggb_similarity.tsv
python3 odgi_similarity_to_newick.py ecoli_pggb_similarity.tsv ecoli_pggb.nhGoes in the tracks array of config.json. See Tracks.
{
"type": "MafTrack",
"trackId": "ecoli_pggb_maf",
"name": "pggb graph: whole-genome alignment (MAF, vs K12)",
"assemblyNames": ["K12"],
"adapter": {
"type": "MafTabixAdapter",
"samples": ["K12", "Sakai", "CFT073", "NCTC86", "IAI39"],
"nhLocation": { "uri": "ecoli_pggb.nh" },
"uri": "ecoli_pggb.maf.bed.gz",
"csi": true
}
}jbrowse add-track-json '{
"type": "MafTrack",
"trackId": "ecoli_pggb_maf",
"name": "pggb graph: whole-genome alignment (MAF, vs K12)",
"assemblyNames": ["K12"],
"adapter": {
"type": "MafTabixAdapter",
"samples": ["K12", "Sakai", "CFT073", "NCTC86", "IAI39"],
"nhLocation": { "uri": "ecoli_pggb.nh" },
"uri": "ecoli_pggb.maf.bed.gz",
"csi": true
}
}'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": "MafTrack",
"trackId": "ecoli_pggb_maf",
"name": "pggb graph: whole-genome alignment (MAF, vs K12)",
"assemblyNames": ["K12"],
"adapter": {
"type": "MafTabixAdapter",
"samples": ["K12", "Sakai", "CFT073", "NCTC86", "IAI39"],
"nhLocation": { "uri": "ecoli_pggb.nh" },
"uri": "ecoli_pggb.maf.bed.gz",
"csi": true
}
}ecoli_pggb.nh, ecoli_pggb.maf.bed.gz are relative to a config.json. Replace each with its URL or its path on this computer.
Drag across the rows, and the menu on release lists each strain the selection covers under two submenus:
- Open aligned genome at the matching region opens that strain's genome at the aligned stretch
- Linear synteny view, K12 vs... opens K12 and that strain as a linear synteny view
Pangenome depth: core versus accessory sequence
odgi depth
counts how many paths cross each K12 window, so core sequence reads the strain
count and sequence only K12 has reads 1. Make 500 bp windows, count, and write a
bigWig:
reflen=$(awk -v p="K12#1#chr" '$1 == p {print $2}' all.fa.gz.fai)
awk -v p="K12#1#chr" -v len="$reflen" -v w=500 \
'BEGIN { for (s = 0; s < len; s += w) { e = s + w; if (e > len) e = len
print p "\t" s "\t" e } }' > depth_windows.bed
in_pggb odgi depth -i "/data/$og" -b /data/depth_windows.bed |
awk -v p="K12#1#chr" -v OFS='\t' '$1 == p && $4 + 0 == $4 { print "chr", $2, $3, $4 }' |
sort -k1,1 -k2,2n > ecoli_pggb_depth.bedgraph
printf 'chr\t%s\n' "$reflen" > chrom.sizes
bedGraphToBigWig ecoli_pggb_depth.bedgraph chrom.sizes ecoli_pggb_depth.bwThe config draws reference lines at the strain count and at 1:
Goes in the tracks array of config.json. See Tracks.
{
"type": "QuantitativeTrack",
"trackId": "ecoli_pggb_depth",
"name": "pggb graph: pangenome depth (paths over K12)",
"assemblyNames": ["K12"],
"adapter": {
"type": "BigWigAdapter",
"uri": "ecoli_pggb_depth.bw"
},
"displayDefaults": {
"encoding": {
"y": {
"axis": { "title": "paths" },
"rules": [
{ "value": 5, "label": "all five strains" },
{ "value": 1, "label": "K12 alone" }
]
}
}
}
}jbrowse add-track ecoli_pggb_depth.bw \
--trackId ecoli_pggb_depth \
--name "pggb graph: pangenome depth (paths over K12)" \
--assemblyNames K12 \
--displayDefaults '{"encoding":{"y":{"axis":{"title":"paths"},"rules":[{"value":5,"label":"all five strains"},{"value":1,"label":"K12 alone"}]}}}' \
--load copyIn 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": "ecoli_pggb_depth",
"name": "pggb graph: pangenome depth (paths over K12)",
"assemblyNames": ["K12"],
"adapter": {
"type": "BigWigAdapter",
"uri": "ecoli_pggb_depth.bw"
},
"displayDefaults": {
"encoding": {
"y": {
"axis": { "title": "paths" },
"rules": [
{ "value": 5, "label": "all five strains" },
{ "value": 1, "label": "K12 alone" }
]
}
}
}
}ecoli_pggb_depth.bw is relative to a config.json. Replace it with its URL or its path on this computer.
The curve doubles over the rrnC and rrnB operons (ribosomal RNA gene copies), because the graph folds those two copies onto each other.
Per-strain presence
odgi pav
splits depth per strain, as the fraction of each window that the strain's path
crosses. Write one bigWig per strain other than K12:
in_pggb odgi pav -i "/data/$og" -b /data/depth_windows.bed > pav.tsv
for strain in Sakai CFT073 NCTC86 IAI39; do
awk -F'\t' -v OFS='\t' -v g="${strain}#1#chr" \
'$5 == g && $6 + 0 == $6 { print "chr", $2, $3, $6 }' pav.tsv |
sort -k1,1 -k2,2n > "pav_${strain}.bedgraph"
bedGraphToBigWig "pav_${strain}.bedgraph" chrom.sizes "ecoli_pggb_pav_${strain}.bw"
doneThe four bigWigs load as one heatmap track, a row per strain, where a white column is a stretch that strain lacks:
Goes in the tracks array of config.json. See Tracks.
{
"type": "MultiQuantitativeTrack",
"trackId": "ecoli_pggb_pav",
"name": "pggb graph: per-strain presence (odgi pav, vs K12)",
"assemblyNames": ["K12"],
"adapter": {
"type": "MultiWiggleAdapter",
"subadapters": [
{
"type": "BigWigAdapter",
"name": "Sakai",
"uri": "ecoli_pggb_pav_Sakai.bw"
},
{
"type": "BigWigAdapter",
"name": "CFT073",
"uri": "ecoli_pggb_pav_CFT073.bw"
},
{
"type": "BigWigAdapter",
"name": "NCTC86",
"uri": "ecoli_pggb_pav_NCTC86.bw"
},
{
"type": "BigWigAdapter",
"name": "IAI39",
"uri": "ecoli_pggb_pav_IAI39.bw"
}
]
},
"displayDefaults": { "mark": "rect" }
}jbrowse add-track-json '{
"type": "MultiQuantitativeTrack",
"trackId": "ecoli_pggb_pav",
"name": "pggb graph: per-strain presence (odgi pav, vs K12)",
"assemblyNames": ["K12"],
"adapter": {
"type": "MultiWiggleAdapter",
"subadapters": [
{
"type": "BigWigAdapter",
"name": "Sakai",
"uri": "ecoli_pggb_pav_Sakai.bw"
},
{
"type": "BigWigAdapter",
"name": "CFT073",
"uri": "ecoli_pggb_pav_CFT073.bw"
},
{
"type": "BigWigAdapter",
"name": "NCTC86",
"uri": "ecoli_pggb_pav_NCTC86.bw"
},
{
"type": "BigWigAdapter",
"name": "IAI39",
"uri": "ecoli_pggb_pav_IAI39.bw"
}
]
},
"displayDefaults": { "mark": "rect" }
}'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": "ecoli_pggb_pav",
"name": "pggb graph: per-strain presence (odgi pav, vs K12)",
"assemblyNames": ["K12"],
"adapter": {
"type": "MultiWiggleAdapter",
"subadapters": [
{
"type": "BigWigAdapter",
"name": "Sakai",
"uri": "ecoli_pggb_pav_Sakai.bw"
},
{
"type": "BigWigAdapter",
"name": "CFT073",
"uri": "ecoli_pggb_pav_CFT073.bw"
},
{
"type": "BigWigAdapter",
"name": "NCTC86",
"uri": "ecoli_pggb_pav_NCTC86.bw"
},
{
"type": "BigWigAdapter",
"name": "IAI39",
"uri": "ecoli_pggb_pav_IAI39.bw"
}
]
},
"displayDefaults": { "mark": "rect" }
}ecoli_pggb_pav_Sakai.bw, ecoli_pggb_pav_CFT073.bw, ecoli_pggb_pav_NCTC86.bw, ecoli_pggb_pav_IAI39.bw are relative to a config.json. Replace each with its URL or its path on this computer.
Open chr:450,000-590,000 with the K12 genes on. The figure shades
chr:501,500-539,000.
Across the shaded span IAI39's row is empty and depth drops by one. The deeper
trough to its right, chr:565,000-585,000, is the prophage that begins at
intD, where depth falls toward the K12 alone line.
Testing the CPZ-55 depth trough with nanopore reads
A depth trough marks a stretch of K12 the other strains lack. Reads from a new isolate show whether it lacks the stretch too. We'll load nanopore reads from E. coli E146, an isolate outside the graph, over CPZ-55, a cryptic prophage only K12 has. Download them:
curl -fO https://ftp.sra.ebi.ac.uk/vol1/fastq/DRR193/DRR193901/DRR193901_1.fastq.gzMap them onto K12:
minimap2 -ax map-ont K12.fa DRR193901_1.fastq.gz | samtools sort -o ecoli_e146_ont.bam
samtools index ecoli_e146_ont.bamGoes in the tracks array of config.json. See Tracks.
{
"trackId": "ecoli_e146_ont",
"name": "E146 nanopore reads (vs K12)",
"uri": "ecoli_e146_ont.bam",
"assemblyNames": ["K12"]
}jbrowse add-track ecoli_e146_ont.bam \
--trackId ecoli_e146_ont \
--name "E146 nanopore reads (vs K12)" \
--assemblyNames K12 \
--load copyIn JBrowse Desktop, or in any running JBrowse Web session, open a view on this track’s assembly, then File → Open track... and, in Add a track from file or URL, enter:
- Main file:
ecoli_e146_ont.bam
Click Next. JBrowse reads the adapter and track type off the file name. Then fill in:
- Track name:
E146 nanopore reads (vs K12) - Assembly:
K12
Click Add.
ecoli_e146_ont.bam is relative to a config.json. Replace it with its URL or its path on this computer.
Type chr:2,554,000-2,570,000. Reads long enough to cross the trough show it as
a single labelled deletion.
The graph as a track
The graph genome view plugin draws the
graph itself as a track of the linear view.
gfa-to-tabix build indexes it by
following the K12 path, and takes the bubbles from pggb's undecomposed VCF,
renamed like the decomposed one:
in_pggb bash -c "bcftools annotate --rename-chrs /data/rename_chrs.tsv \
/data/pggb/*.smooth.final.K12.vcf \
| bcftools sort -Oz -o /data/ecoli_pggb_snarls.vcf.gz && tabix -C -p vcf /data/ecoli_pggb_snarls.vcf.gz"
gfa-to-tabix build pggb/*.smooth.final.gfa -o ecoli_pggb --reference K12 --snarls ecoli_pggb_snarls.vcf.gzThe command writes the segments and links, a coarse tier with variants under 50
bp folded into the reference, and ecoli_pggb.config.json, all in the contig
layout. The uris below are our hosted copy.
Goes in the tracks array of config.json. See Tracks.
{
"type": "GraphTrack",
"trackId": "ecoli_pggb_segments",
"name": "pggb graph segments (whole graph, by locus)",
"assemblyNames": ["K12"],
"adapter": {
"type": "RgfaTabixAdapter",
"uri": "https://jbrowse.org/demos/ecoli_pangenome/ecoli_pggb",
"format": 2,
"coarse": {
"uri": "https://jbrowse.org/demos/ecoli_pangenome/ecoli_pggb.fold50",
"format": 2,
"aboveBpPerPx": 5
}
},
"displayDefaults": {
"labels": { "name": { "show": false }, "description": { "show": false } }
},
"displays": [
{ "type": "LinearGraphDisplay" },
{ "type": "LinearBasicDisplay" }
]
}jbrowse add-track-json '{
"type": "GraphTrack",
"trackId": "ecoli_pggb_segments",
"name": "pggb graph segments (whole graph, by locus)",
"assemblyNames": ["K12"],
"adapter": {
"type": "RgfaTabixAdapter",
"uri": "https://jbrowse.org/demos/ecoli_pangenome/ecoli_pggb",
"format": 2,
"coarse": {
"uri": "https://jbrowse.org/demos/ecoli_pangenome/ecoli_pggb.fold50",
"format": 2,
"aboveBpPerPx": 5
}
},
"displayDefaults": {
"labels": { "name": { "show": false }, "description": { "show": false } }
},
"displays": [
{ "type": "LinearGraphDisplay" },
{ "type": "LinearBasicDisplay" }
]
}'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": "GraphTrack",
"trackId": "ecoli_pggb_segments",
"name": "pggb graph segments (whole graph, by locus)",
"assemblyNames": ["K12"],
"adapter": {
"type": "RgfaTabixAdapter",
"uri": "https://jbrowse.org/demos/ecoli_pangenome/ecoli_pggb",
"format": 2,
"coarse": {
"uri": "https://jbrowse.org/demos/ecoli_pangenome/ecoli_pggb.fold50",
"format": 2,
"aboveBpPerPx": 5
}
},
"displayDefaults": {
"labels": { "name": { "show": false }, "description": { "show": false } }
},
"displays": [
{ "type": "LinearGraphDisplay" },
{ "type": "LinearBasicDisplay" }
]
}Open track... → Add pangenome graph track adds the same track from an empty session:
Type chr:1,292,500-1,307,500. Zoomed out past five bp per pixel, the track
draws the coarse tier, the graph with variants under 50 bp folded into the
reference, with charcoal where the strains differ. The tier also loads as a
separate track:
Goes in the tracks array of config.json. See Tracks.
{
"type": "GraphTrack",
"trackId": "ecoli_pggb_tier50",
"name": "pggb graph, variants under 50 bp folded (coarse tier)",
"assemblyNames": ["K12"],
"adapter": {
"type": "RgfaTabixAdapter",
"uri": "https://jbrowse.org/demos/ecoli_pangenome/ecoli_pggb.fold50",
"format": 2
},
"displays": [
{ "type": "LinearGraphDisplay" },
{ "type": "LinearBasicDisplay" }
]
}jbrowse add-track-json '{
"type": "GraphTrack",
"trackId": "ecoli_pggb_tier50",
"name": "pggb graph, variants under 50 bp folded (coarse tier)",
"assemblyNames": ["K12"],
"adapter": {
"type": "RgfaTabixAdapter",
"uri": "https://jbrowse.org/demos/ecoli_pangenome/ecoli_pggb.fold50",
"format": 2
},
"displays": [
{ "type": "LinearGraphDisplay" },
{ "type": "LinearBasicDisplay" }
]
}'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": "GraphTrack",
"trackId": "ecoli_pggb_tier50",
"name": "pggb graph, variants under 50 bp folded (coarse tier)",
"assemblyNames": ["K12"],
"adapter": {
"type": "RgfaTabixAdapter",
"uri": "https://jbrowse.org/demos/ecoli_pangenome/ecoli_pggb.fold50",
"format": 2
},
"displays": [
{ "type": "LinearGraphDisplay" },
{ "type": "LinearBasicDisplay" }
]
}To set the tier beside the strains:
- In the tier track's menu, Display types → Feature display draws the tier's segments as a row at any zoom, marking where the strains differ.
- Turn on the MAF track between the tier and the graph. A strain's row breaks across each bubble it skips, so the MAF names the strains.
- Add a second copy of the K12 genes and keep the insertion sequences with
Filter by → Edit filters... and
startsWith(feature.name || '','ins').
A segment K12 lacks has coordinates on the strain that has it, and its
right-click menu opens that strain's genome there. Type
chr:1,004,500-1,004,961, right-click the 75 bp CFT073 segment and pick Open
in CFT073.
Coloring each segment by how many strains have it
The index records which strains pass through each segment, and a feature track over the same files colors each segment by that count:
Goes in the tracks array of config.json. See Tracks.
{
"type": "FeatureTrack",
"trackId": "ecoli_pggb_carriage",
"name": "pggb graph: strains per segment",
"assemblyNames": ["K12"],
"adapter": {
"type": "RgfaTabixAdapter",
"uri": "https://jbrowse.org/demos/ecoli_pangenome/ecoli_pggb",
"format": 2
},
"displayDefaults": {
"displayMode": "collapsed",
"labels": { "name": { "show": false }, "description": { "show": false } },
"encoding": {
"color": {
"field": "sampleCount",
"scale": {
"domain": ["5", "4", "3", "2", "1"],
"range": ["#bdbdbd", "#fed976", "#feb24c", "#fd8d3c", "#e31a1c"],
"names": [
"All 5 strains (core)",
"4 strains",
"3 strains",
"2 strains",
"1 strain (private)"
]
},
"legend": { "title": "Strains" }
}
}
}
}jbrowse add-track-json '{
"type": "FeatureTrack",
"trackId": "ecoli_pggb_carriage",
"name": "pggb graph: strains per segment",
"assemblyNames": ["K12"],
"adapter": {
"type": "RgfaTabixAdapter",
"uri": "https://jbrowse.org/demos/ecoli_pangenome/ecoli_pggb",
"format": 2
},
"displayDefaults": {
"displayMode": "collapsed",
"labels": { "name": { "show": false }, "description": { "show": false } },
"encoding": {
"color": {
"field": "sampleCount",
"scale": {
"domain": ["5", "4", "3", "2", "1"],
"range": ["#bdbdbd", "#fed976", "#feb24c", "#fd8d3c", "#e31a1c"],
"names": [
"All 5 strains (core)",
"4 strains",
"3 strains",
"2 strains",
"1 strain (private)"
]
},
"legend": { "title": "Strains" }
}
}
}
}'In JBrowse Desktop, or in any running JBrowse Web session, open a view on this track’s assembly, then File → Open track..., choose Add pangenome graph track, and fill in:
- File type:
Indexed GFA (gfa-to-tabix .segs.bed.gz or .graph.bed.gz) - Path:
https://jbrowse.org/demos/ecoli_pangenome/ecoli_pggb.graph.bed.gz - Track name:
pggb graph: strains per segment - Assembly:
K12
Type chr:1,299,499-1,300,693, the IS5 element. Advanced → Edit plot... in
the track menu shows the encoding.color block as text and applies edits live.
Reproduce it end to end
build_ecoli_pangenome_graph.sh
runs every step above except the E146 read mapping. The script:
- Downloads the assemblies and builds the graph with pggb.
- Writes the synteny, variant, MAF, depth, presence and graph tracks from it.
- Downloads JBrowse and writes a
config.jsonwith the assemblies, their gene tracks, every other track on this page, and a default session.
curl -fO https://raw.githubusercontent.com/GMOD/jbrowse-components/main/scripts/build_ecoli_pangenome_graph.sh
bash build_ecoli_pangenome_graph.sh
npx --yes serve ecoli_pangenome_graph_build/jbrowse2CONTAINER=singularity forces the container runtime. To add genomes, add rows
to the strain table in the script; wfmash time grows with the square of the
genome count.
See also
- Tutorial: Pangenome (Minigraph-Cactus)
- Tutorial: Pangenome (HPRC) part 1: the graph's alleles and the haplotypes that have them
- Tutorial: Synteny visualization (all-vs-all minimap2)
- Tutorial: Synteny from gene symbols (primates and E. coli)
- User guide: Graph genome view
- User guide: MAF track
- User guide: Variant sample rows
- Developer guide: PIF (Pairwise Indexed Format)
External links
Feedback on this tutorial is welcome: contact us.