
This paired tutorial is generated from the same sources as the website. Code blocks are shown without executing during vignette rendering; the shared documentation script verifies the examples directly. Examples prefer bundled or source-backed data; deliberate diagnostic errors are labelled. Inputs marked as repository files require the checkout or input bundle. Each chapter provides its own preparation; start with the first-plot lesson.
R ≥4.1.0 and ggplot2 ≥4.0.0 are required. Installation and examples target v0.16.6.
install.packages(c("remotes", "dplyr", "purrr", "tidyr"))
remotes::install_github("DangJem/[email protected]")
library(ggchord)
packageVersion("ggchord")This tutorial documents ggchord 0.16.6. The installation above pins its formal tag; see GitHub Releases for release assets. For a source checkout, run from its repository root:
Use the loading route appropriate to your source; you do not need both installation paths. Record the version when reproducing a plot. This lesson needs neither optional Bioconductor packages nor Biopython.
Declared lower bounds are R 4.1.0 and ggplot2 4.0.0. Release checks and actual platform versions are recorded in the release acceptance report; these do not certify every dependency combination. Optional readers require their documented dependencies.
Starting with chord follows ggchord’s development order. See the learning map. The two-sequence exercise below is an optional environment check rather than a substitute for the modules.
Sequences need accver/length; links need two IDs and four endpoints; genes need accver/start/end/strand. Module I’s first lesson explains real tables, optional fields and BLAST naming.
Run the whole folded block, then inspect the figure. Bundled source-backed records need no external files or BLAST run.
library(ggchord)
library(dplyr)
data(seq_data_example)
data(gene_data_example)
data(ribbon_data_example)
ids <- c("MT108731.1", "MT118296.1")
first_sequences <- seq_data_example |>
filter(accver %in% ids) |>
select(accver, length)
first_genes <- gene_data_example |>
filter(accver %in% ids) |>
transmute(accver, start, end, strand = source_strand)
first_links <- ribbon_data_example |>
filter(qaccver %in% ids, saccver %in% ids) |>
select(qaccver, saccver, qstart, qend, sstart, send)
first_colours <- setNames(c("#E64B35", "#233E4A"), ids)
p_first <- ggchord(
seq_data = first_sequences, ribbon_data = first_links,
gene_data = first_genes
) +
geom_link_ribbon(fill = "#82B6AD", alpha = .55) +
geom_seq(colour = "#233E4A", show.legend = FALSE) +
geom_gene(aes(gene_fill = accver), position = position_strand(.10), width = .045) +
geom_seq_label(size = 4) +
coord_collinear() +
scale_gene_fill_manual(
values = first_colours, name = "Sequence",
guide = guide_ggchord_legend(nrow = 1, theme = theme(
legend.title = element_text(size = 13, face = "bold"), legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"), legend.margin = margin(l = 7, unit = "mm")
))
) +
scale_seq_position_continuous(n.breaks = 3, minor_breaks = NULL) +
theme_ggchord_publication(axis.text = element_text(size = 11)) +
theme(
legend.position = "bottom",
plot.background = element_rect(fill = "#FFFCF5", colour = NA),
panel.background = element_blank()
)
p_first
Arrows retain biological strands; colours identify sequences only. Ribbons are existing comparison intervals, not new homology results. This small linear example makes positions easy to read; the full chord module starts with backbones.
Run this only after the block above has created p_first:
Use the same 10×5 in dimensions for preview and export; see output.
For missing objects, run the preparation block. For missing functions, check the package version. getwd() determines the save directory. Optional readers need dependencies only in their own topics; basic modules do not require Bioconductor or Biopython.
Choose a layout for your question. The examples below compare three views using source-backed bundled records.
| Task | Preferred view | Avoid this misreading |
|---|---|---|
| Overall relationships among several sequences | Chord | Crossings alone are not evidence of inversion |
| Annotations on one plasmid or circular sequence | Circular | Multiple sequences do not fit a single-sequence coordinate |
| Positions, gene order and local neighbourhoods | Collinear | Display alignment or flipping is not a biological conclusion |
All three consume existing data; none computes a new BLAST alignment. Try the minimal combinations below, then follow the raw-record workflow, complete plasmid map or local homolog comparison.
In a fresh R session, run the code below, then follow this page in order. Colours and layout are optional display choices; inputs select only the fields needed.
library(ggchord)
library(dplyr)
data(seq_data_example)
data(ribbon_data_example)
data(gene_data_example)
seqs <- seq_data_example |>
select(accver, length)
links <- ribbon_data_example |>
select(qaccver, saccver, qstart, qend, sstart, send)
# The fixture preserves biological strands separately from its old display strands.
genes <- gene_data_example |>
transmute(accver, start, end, strand = source_strand)
# Optional appearance choices; none of these columns or scales is required input.
library(ggchord)
seq_palette <- setNames(
c("#E64B35", "#233E4A", "#269B82", "#D59B17"),
c("MT108731.1", "MT118296.1", "OQ646790.1", "OR222515.1")
)
gene_palette <- c("+" = "#CE1536", "-" = "#346B9D", "." = "#8291A6")
library(ggchord)
feature_palette <- c(
CDS = "#993366", promoter = "#31849B", rep_origin = "#F1E51D",
primer_bind = "#A020F0", misc_feature = "#A6ACB3", protein_bind = "#31849B"
)
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}
library(dplyr)
data(plasmid_example_pUC19)
plasmid_seq <- plasmid_example_pUC19 |>
select(accver, length)
feature_source <- find_common_features(plasmid_example_pUC19)
features <- feature_source |>
select(accver, start, end, strand, type, anno)Run the preparation above once. Each object below is a normal
composable ggplot; print it or use ggsave().
p_chord <- ggchord(seq_data = seqs, ribbon_data = links, gene_data = genes) +
geom_link_ribbon(aes(ribbon_fill = qaccver), alpha = .7) +
scale_ribbon_fill_manual(values = seq_palette, guide = "none") +
geom_seq(show.legend = FALSE) +
geom_gene(position = position_strand(.1), width = .04) +
geom_seq_label(size = 4, seq_label_radius = .84, seq_label_hjust = .5) +
coord_chord() +
scale_seq_colour_manual(values = seq_palette) +
scale_gene_fill_manual(values = gene_palette) +
doc_theme
p_chord
Links are coloured by query sequence ID using the backbone category
palette; this does not encode identity or homolog groups. See the raw-record workflow for colour mapped to
reported identity. coord_chord() controls global rotation
and fitting. Sequence-local radius, curvature, gap and orientation
belong to geom_seq(). A NULL link control point uses the
historical origin-based chord route; it does not mean a universal
straight link. Ribbon avoidance defaults to uniform, line
avoidance to none.
anchors <- genes |>
group_by(accver) |>
slice_max(end - start, n = 1, with_ties = FALSE) |>
ungroup() |>
select(accver, start, end)
p_collinear <- ggchord(seq_data = seqs, ribbon_data = links, gene_data = genes) +
geom_link_ribbon(aes(ribbon_fill = qaccver), alpha = .7) +
scale_ribbon_fill_manual(values = seq_palette, guide = "none") +
geom_seq() +
geom_gene(position = position_strand(.1)) +
geom_seq_label(size = 4) +
coord_collinear(anchor = anchors, align = "center", orientation = "auto") +
doc_axis() +
scale_seq_colour_manual(values = seq_palette) +
scale_gene_fill_manual(values = gene_palette) +
doc_theme
p_collinear
Each sequence is anchored to its own longest known gene for display;
this does not assert homology. anchor aligns specified
intervals; orientation = "auto" chooses supported
orientations using available evidence. Inspect orientation diagnostics
rather than assuming biological inversion. Ribbons connect adjacent
displayed rows by default; use adjacent_only = FALSE or
explicit pairs to retain others. Non-adjacent outlines
default to dashed; explicit styles take priority. These rules differ
from chord mode. Focus and segments preserve source
coordinates for cropped or locally reversed views.
circle_features <- features |>
filter(type %in% c("CDS", "rep_origin"))
p_circular <- ggchord(seq_data = plasmid_seq) +
geom_seq() +
geom_feature(aes(feature_fill = type), data = circle_features, position = position_plasmid()) +
geom_feature_label_repel(data = circle_features, position = position_plasmid(), size = 4) +
geom_seq_center_label() +
scale_feature_fill_manual(values = feature_palette, breaks = unique(circle_features$type)) +
coord_circular() +
doc_theme
p_circular
This example selects CDS and the replication origin for display,
retaining the full sequence bp axis. coord_circular()
requires a single sequence. General features can carry unknown strand.
geom_feature_plasmid() is a convenience preset with its own
defaults; choose it explicitly when those defaults fit the task. Primer
and restriction layers are described in recognition.
First count sequences: use circular for one annotated sequence, chord for the overall link pattern, and collinear for positions, distances or neighbourhoods.
Learn the tables before selecting layers. Chord comparison was ggchord’s original core view. Its early BLAST workflows explain the compact field names retained today. A multi-sequence chord view displays existing interval comparisons among sequences; drawing does not run a multiple-sequence alignment algorithm.
| Table | Required columns | One row represents |
|---|---|---|
| seq_data | accver, length | One sequence and its full length in bp |
| ribbon_data | qaccver, saccver, qstart, qend, sstart, send | One observed pair of aligned intervals |
| gene_data | accver, start, end, strand | One directed gene interval |
Backbones alone need neither links nor genes. Use
geom_feature() for annotations with unknown strand rather
than assigning a direction.
accver originated from accession.version, but any stable
contig identifier is allowed; versions are not added or removed.
q means query and s means subject. Both IDs
must occur in the sequence table.
pident is percentage identity, needed only for identity
mapping/filtering. Alignment length differs from full
sequence length. Keep evalue,
bitscore, qlen and slen when
required by an analysis. Gene anno is optional annotation
text; supply it for labels. Additional category columns can feed
aes() without becoming mandatory inputs.
library(ggchord)
library(dplyr)
seq_data_example |> select(accver, length) |> slice_head(n = 3)
ribbon_data_example |> select(qaccver, saccver, qstart, qend, sstart, send) |> slice_head(n = 3)
gene_data_example |> transmute(accver, start, end, strand = source_strand, anno) |> slice_head(n = 3)These are bundled source-backed records. This historical teaching
fixture stores biological strand separately in
source_strand; restore it explicitly. Newly read GFF3
annotations use their ordinary strand column.
Use one-based inclusive bp coordinates. Genes have ordered bounds with a separate strand. Endpoint order can represent reverse alignments; do not sort every link. Maximum feature endpoints do not establish full sequence lengths. See the coordinate contract.
Continue to 1.2 From analysis results to input tables.
Start with sequences and analysis results, then read them with ggchord. Missing comparisons must not be invented to populate a figure.
Extract the tutorial
bundle so the working directory contains examples/.
FASTA supplies full lengths; GFF3 supplies intervals and strands. Retain
source records separately from display tables.
# Optional appearance choices; none of these columns or scales is required input.
library(ggchord)
seq_palette <- setNames(
c("#E64B35", "#233E4A", "#269B82", "#D59B17"),
c("MT108731.1", "MT118296.1", "OQ646790.1", "OR222515.1")
)
gene_palette <- c("+" = "#CE1536", "-" = "#346B9D", "." = "#8291A6")
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}library(ggchord)
library(dplyr)
library(purrr)
library(tidyr)
# Original repository records; extract the tutorial input bundle first.
show_sequences <- read_fasta_lengths(files = "examples/fasta/*.fna") |>
select(accver, length)
show_annotations <- read_gff3(files = "examples/gff3/*.gff3", unstranded = "keep")
show_genes <- show_annotations |>
filter(type == "CDS") |>
select(accver, start, end, strand, anno)
show_alignments <- read_blast(files = "examples/blastn-standard/*.tsv")
# At most three substantial observed HSPs per pair; no coordinates are invented.
show_links <- show_alignments |>
filter(length >= 900, pident >= 75) |>
group_by(qaccver, saccver) |>
slice_max(length, n = 3, with_ties = FALSE) |>
ungroup()
# Select up to four source CDS per sequence, spread across annotation order.
# No feature is moved or invented; keep complete source tables separately.
show_features <- show_genes |>
filter(end - start + 1 >= 500) |>
group_by(accver) |>
arrange(start, .by_group = TRUE) |>
mutate(display_bin = ntile(row_number(), 4), informative = !is.na(anno) & anno != "hypothetical protein") |>
group_by(accver, display_bin) |>
arrange(desc(informative), desc(end - start), .by_group = TRUE) |>
slice_head(n = 1) |>
ungroup() |>
select(accver, start, end, strand, anno)
# Also retain well-separated negative-strand CDS, if not already represented.
show_reverse <- show_genes |>
filter(strand == "-", end - start + 1 >= 500) |>
group_by(accver) |>
arrange(start, .by_group = TRUE) |>
slice(c(1, n())) |>
ungroup()
show_features <- bind_rows(show_features, show_reverse) |>
distinct(accver, start, end, strand, .keep_all = TRUE) |>
arrange(accver, start)
show_labels <- show_features
show_optimization <- optimize_ggchord_layout(
show_sequences, show_links,
optimize = "orientation", weight = "length"
)show_alignments retains 32 observed HSPs and
show_links selects 12. show_genes retains 266
CDS; show_features selects CDS of at least 500 bp from four
groups across annotation order per sequence, additionally retaining
source-backed negative-strand endpoints. Selection neither reruns
alignment nor establishes functional homology.
For coordinate inspection, reshape the four endpoint columns with tidyr while retaining the original link table:
These producer commands are for analysts with BLAST installed and their own input files; the website builder does not execute them.
Without a custom field list, outfmt 6 uses the standard 12-column table; 7 adds comments. Omitting outfmt entirely requests another format. Query/subject IDs map to qaccver/saccver. Consult the NCBI field documentation and nucleotide/protein guide. Historical tutorial HSPs are converted records, not newly run BLAST results.
| Source | Entry point | Check |
|---|---|---|
| Existing table | as_ggchord_seq/feature/link | Explicit role mapping; retain the original |
| BED, PAF | read_bed, read_paf | Convert zero-based half-open coordinates only once |
| GenBank | read_genbank | Biopython required; preserve record and segmented-location provenance |
| GRanges, Seqinfo | as_ggchord_feature, as_ggchord_seq | Full lengths from Seqinfo; unknown strand stays unknown |
| GInteractions | as_ggchord_link, as_ggchord_seq | First anchor is query; full length context is still required |
These adapters address the request in issue #2. Current code provides the entries; the historical discussion does not certify every current combination. Missing optional packages are explicitly skipped locally. See format conversion for runnable optional-object examples and limitations.
Continue to 1.3 Build from backbones.
Complete each small step before the flagship example. All four views reuse the same records, making the effect of each added layer visible.
# Optional appearance choices; none of these columns or scales is required input.
library(ggchord)
seq_palette <- setNames(
c("#E64B35", "#233E4A", "#269B82", "#D59B17"),
c("MT108731.1", "MT118296.1", "OQ646790.1", "OR222515.1")
)
gene_palette <- c("+" = "#CE1536", "-" = "#346B9D", "." = "#8291A6")
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}
library(ggchord)
library(dplyr)
library(purrr)
library(tidyr)
# Original repository records; extract the tutorial input bundle first.
show_sequences <- read_fasta_lengths(files = "examples/fasta/*.fna") |>
select(accver, length)
show_annotations <- read_gff3(files = "examples/gff3/*.gff3", unstranded = "keep")
show_genes <- show_annotations |>
filter(type == "CDS") |>
select(accver, start, end, strand, anno)
show_alignments <- read_blast(files = "examples/blastn-standard/*.tsv")
# At most three substantial observed HSPs per pair; no coordinates are invented.
show_links <- show_alignments |>
filter(length >= 900, pident >= 75) |>
group_by(qaccver, saccver) |>
slice_max(length, n = 3, with_ties = FALSE) |>
ungroup()
# Select up to four source CDS per sequence, spread across annotation order.
# No feature is moved or invented; keep complete source tables separately.
show_features <- show_genes |>
filter(end - start + 1 >= 500) |>
group_by(accver) |>
arrange(start, .by_group = TRUE) |>
mutate(display_bin = ntile(row_number(), 4), informative = !is.na(anno) & anno != "hypothetical protein") |>
group_by(accver, display_bin) |>
arrange(desc(informative), desc(end - start), .by_group = TRUE) |>
slice_head(n = 1) |>
ungroup() |>
select(accver, start, end, strand, anno)
# Also retain well-separated negative-strand CDS, if not already represented.
show_reverse <- show_genes |>
filter(strand == "-", end - start + 1 >= 500) |>
group_by(accver) |>
arrange(start, .by_group = TRUE) |>
slice(c(1, n())) |>
ungroup()
show_features <- bind_rows(show_features, show_reverse) |>
distinct(accver, start, end, strand, .keep_all = TRUE) |>
arrange(accver, start)
show_labels <- show_features
show_optimization <- optimize_ggchord_layout(
show_sequences, show_links,
optimize = "orientation", weight = "length"
)p_learn_seq <- ggchord(seq_data = show_sequences) +
geom_seq() +
geom_seq_label(size = 4) +
scale_seq_colour_manual(
values = seq_palette, name = "Sequence",
guide = sequence_guide
) +
scale_seq_position_continuous(breaks = c(0, 20000, 40000), minor_breaks = NULL) +
coord_chord() +
doc_theme
p_learn_seq
geom_seq() draws backbones,
geom_seq_label() identifies sequences, and
coord_chord() selects the global view. No links or genes
are required yet.
p_learn_links <- p_learn_seq +
geom_link_ribbon(
aes(ribbon_fill = pident),
data = show_links,
adjacent_only = FALSE, link_avoid = "smooth"
) +
scale_ribbon_fill_gradientn(
colours = identity_palette, values = identity_values, limits = c(0, 100),
breaks = identity_breaks, name = "Identity (%)", guide = identity_guide
)
p_learn_links
Identity belongs in aes and a scale. Explicitly retaining non-neighbour links keeps the same link data when replacing coordinates later.
p_learn_genes <- p_learn_links +
geom_gene(data = show_features, position = position_strand(0.08)) +
scale_gene_fill_manual(values = gene_palette, breaks = c("+", "-"), name = "Strand") +
guides(gene_fill = strand_guide)
p_learn_genes
position_strand() separates positive and negative
strands. Display selected real annotations while retaining every source
CDS separately.
p_learn_labels <- p_learn_genes +
geom_gene_label_repel(
data = show_features, label_layout = "arc", label_wrap = 24,
label_max_lines = 3, position = position_strand(0.08), size = 3
)
p_learn_labels
The arrow and repel-label layers receive the same feature table without another label filter. Renaming, deleting or reordering records can change meaning; retain clear intermediate objects.
Continue to 1.4 Direction, radius, curvature and clearance.
These controls have different owners: geoms configure sequence geometry, Positions place local features, coords select the global view, and scales/guides control encodings.
# Optional appearance choices; none of these columns or scales is required input.
library(ggchord)
seq_palette <- setNames(
c("#E64B35", "#233E4A", "#269B82", "#D59B17"),
c("MT108731.1", "MT118296.1", "OQ646790.1", "OR222515.1")
)
gene_palette <- c("+" = "#CE1536", "-" = "#346B9D", "." = "#8291A6")
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}
library(ggchord)
library(dplyr)
library(purrr)
library(tidyr)
# Original repository records; extract the tutorial input bundle first.
show_sequences <- read_fasta_lengths(files = "examples/fasta/*.fna") |>
select(accver, length)
show_annotations <- read_gff3(files = "examples/gff3/*.gff3", unstranded = "keep")
show_genes <- show_annotations |>
filter(type == "CDS") |>
select(accver, start, end, strand, anno)
show_alignments <- read_blast(files = "examples/blastn-standard/*.tsv")
# At most three substantial observed HSPs per pair; no coordinates are invented.
show_links <- show_alignments |>
filter(length >= 900, pident >= 75) |>
group_by(qaccver, saccver) |>
slice_max(length, n = 3, with_ties = FALSE) |>
ungroup()
# Select up to four source CDS per sequence, spread across annotation order.
# No feature is moved or invented; keep complete source tables separately.
show_features <- show_genes |>
filter(end - start + 1 >= 500) |>
group_by(accver) |>
arrange(start, .by_group = TRUE) |>
mutate(display_bin = ntile(row_number(), 4), informative = !is.na(anno) & anno != "hypothetical protein") |>
group_by(accver, display_bin) |>
arrange(desc(informative), desc(end - start), .by_group = TRUE) |>
slice_head(n = 1) |>
ungroup() |>
select(accver, start, end, strand, anno)
# Also retain well-separated negative-strand CDS, if not already represented.
show_reverse <- show_genes |>
filter(strand == "-", end - start + 1 >= 500) |>
group_by(accver) |>
arrange(start, .by_group = TRUE) |>
slice(c(1, n())) |>
ungroup()
show_features <- bind_rows(show_features, show_reverse) |>
distinct(accver, start, end, strand, .keep_all = TRUE) |>
arrange(accver, start)
show_labels <- show_features
show_optimization <- optimize_ggchord_layout(
show_sequences, show_links,
optimize = "orientation", weight = "length"
)show_optimization optimizes display orientation only,
preserving sequence order and weighting observed alignment lengths.
Inspect seq_orientation and score_before/score_after. A display flip is
not a new biological inversion result; the heuristic does not guarantee
a global optimum.
show_curvature <- c(
"MT108731.1" = 0, "MT118296.1" = 0.85,
"OQ646790.1" = 1.1, "OR222515.1" = -0.4
)
show_radius <- c(
"MT108731.1" = 1, "MT118296.1" = 1.05,
"OQ646790.1" = 0.95, "OR222515.1" = 1.08
)
p_show_chord <- ggchord(show_sequences, show_links, show_features) +
geom_link_ribbon(aes(ribbon_fill = pident), link_avoid = "smooth", alpha = 0.58, linewidth = 0.18) +
geom_seq(
seq_orientation = show_optimization$seq_orientation,
seq_curvature = show_curvature, seq_radius = show_radius,
seq_gap = 0.06, linewidth = 1.1
) +
geom_gene(position = position_strand(0.07), width = 0.035) +
geom_gene_label_repel(
data = show_labels, label_layout = "radial", label_wrap = 24,
size = 3.1, label_max_lines = 3, position = position_strand(0.07)
) +
geom_seq_label(size = 4, seq_label_radius = 0.84, seq_label_hjust = 0.5) +
scale_seq_colour_manual(
values = seq_palette, name = "Sequence",
guide = sequence_guide
) +
scale_gene_fill_manual(values = gene_palette, breaks = c("+", "-"), name = "Strand") +
scale_ribbon_fill_gradientn(
colours = identity_palette, values = identity_values,
limits = c(0, 100), breaks = identity_breaks, name = "Identity (%)", guide = identity_guide
) +
guides(gene_fill = strand_guide) +
scale_seq_position_continuous(breaks = c(0, 20000, 40000), minor_breaks = NULL) +
coord_chord(rotation = 50) +
doc_theme +
labs(
title = "Four genomes, one connected view",
subtitle = "Signed curvature and radius · selected strand-aware CDS · local ribbon clearance"
)
p_show_chord
Named vectors match sequence IDs. Curvature 0 is straight, signed values bend in opposite directions, and magnitude changes bow depth. Radius changes sequence placement. The result combines automatic orientation, varying radius, zero/positive/negative curvature and local clearance without changing source coordinates.
link_avoid = "uniform" clears the whole endpoint
interval. "smooth" retreats locally near feature polygons;
"none" disables feature avoidance. Automatic clearance
requires link_gap = NULL; a fixed number overrides it.
Clearance considers feature geometry, not text, and does not
automatically route around intervening sequence rows.
Identity uses a long left-side colourbar; sequence and strand keys remain below. Explicit strand overrides mirror the arrow glyphs. See styling for guide responsibilities. Save at 12×10 in, then review 1.7 The complete showcase.
Reduce link clutter while retaining provenance: filtering, deduplication, merging and bundling have different meanings.
In a fresh R session, run the code below, then follow this page in
order. Colours and layout are optional display choices; inputs select
only the fields needed. For file-based examples, first extract the input
bundle so the working directory contains examples/.
library(ggchord)
library(dplyr)
data(seq_data_example)
data(ribbon_data_example)
data(gene_data_example)
seqs <- seq_data_example |>
select(accver, length)
links <- ribbon_data_example |>
select(qaccver, saccver, qstart, qend, sstart, send)
# The fixture preserves biological strands separately from its old display strands.
genes <- gene_data_example |>
transmute(accver, start, end, strand = source_strand)
# Optional appearance choices; none of these columns or scales is required input.
library(ggchord)
seq_palette <- setNames(
c("#E64B35", "#233E4A", "#269B82", "#D59B17"),
c("MT108731.1", "MT118296.1", "OQ646790.1", "OR222515.1")
)
gene_palette <- c("+" = "#CE1536", "-" = "#346B9D", "." = "#8291A6")
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}
library(dplyr)
sequence_source <- read_fasta_lengths(files = "examples/fasta/*.fna", source_file = TRUE)
seq_real <- sequence_source |>
select(accver, length)
annotation_source <- read_gff3(files = "examples/gff3/*.gff3", unstranded = "keep", source_file = TRUE)
feature_real <- annotation_source |>
select(accver, start, end, strand, type, anno)
alignment_source <- read_blast(files = "examples/blastn-standard/*.tsv", source_file = TRUE)
link_real <- alignment_source |>
select(qaccver, saccver, qstart, qend, sstart, send, pident, length)
real_validation <- validate_ggchord_data(seq_real, link_real)
real_validation
real_prepared <- prepare_ggchord_data(seq_real, link_real,
features = list(annotation = feature_real)
)
real_filtered <- filter_ggchord_ribbons(link_real, min_pident = 80, min_length = 500)
# Explicit display subset: the six longest CDS per sequence; raw annotations stay intact.
real_display_features <- feature_real |>
group_by(accver) |>
slice_max(end - start + 1, n = 6, with_ties = FALSE) |>
ungroup()
real_labels <- real_display_features |>
filter(anno != "hypothetical protein") |>
group_by(accver) |>
slice_head(n = 2) |>
ungroup()
real_filtered$reportUse the real-record workflow to
create seq_real, link_real and
real_filtered. Keep its original tables and filter
report.
unique_links <- deduplicate_ggchord_ribbons(real_filtered$data,
by = "overlap",
min_reciprocal_overlap = .9
)
merged_links <- merge_ggchord_ribbons(unique_links$data,
max_gap = 100,
min_pident_difference = 2
)
bundled_links <- bundle_ggchord_ribbons(link_real, seq_real, bins = 30)
p_density <- ggchord(seq_real, link_real, validate = "none") +
geom_seq(show.legend = FALSE) +
geom_seq_label(size = 4, seq_label_radius = .84, seq_label_hjust = .5) +
stat_ribbon_density(aes(ribbon_alpha = after_stat(density)), fill = "#269B82") +
scale_ribbon_alpha_continuous(
name = "Relative density", range = c(.1, .6),
guide = guide_ggchord_legend(position = "bottom", nrow = 1, theme = doc_legend_theme)
) +
scale_seq_colour_manual(values = seq_palette) +
coord_chord() +
doc_theme
p_bundle <- ggchord(seq_real, link_real, validate = "none") +
geom_seq() +
stat_ribbon_bundle(bins = 30, fill = "#269B82") +
coord_chord() +
doc_theme
p_density| Metric | Observed output |
|---|---|
| Filtered HSPs | 8 |
| After overlap deduplication | 8 |
| Merged display intervals | 8 |
| Bundle output rows | 28 |
Filtering keeps observations meeting declared thresholds.
Reciprocal-overlap deduplication selects a representative existing
observation; it does not sum identities. Merging combines nearby
compatible intervals into a display envelope. Bundling groups nearby
query/subject locations into an aggregate. Inspect each
$report and source-row membership, especially when extra
metadata conflict. The merged or bundled envelope is not a newly
computed nucleotide alignment. Estimated lengths retain their flags;
unobserved identity is never invented.
stat_ribbon_density() maps a computed density, and
stat_ribbon_bundle() performs its explicit aggregation
during build. The ordinary ribbon geom still draws one ribbon per row.
Interpret count, reported length and identity weighting in their own
units; more opaque ribbons need not mean stronger independently
validated homology. A point anchor without direction evidence cannot
acquire an aggregate orientation merely because its endpoints are
equal.
After this page’s preparation code:
# Midpoints summarize existing HSPs for display; they are not new alignments.
points <- ribbon_data_example |>
transmute(
qaccver, saccver,
qpos = floor((qstart + qend) / 2),
spos = floor((sstart + send) / 2),
pident
)
p_points <- ggchord(seq_data = seqs) +
geom_seq() +
geom_seq_label(size = 4) +
geom_link_line(aes(link_colour = pident),
data = points,
arrow = arrow(length = unit(2, "mm")), linewidth = .7
) +
scale_link_colour_gradientn(
colours = identity_palette, values = identity_values,
limits = c(0, 100), breaks = identity_breaks,
name = "Identity (%)", guide = guide_ggchord_colourbar(
available_aes = "link_colour",
position = "bottom", direction = "horizontal",
theme = doc_legend_theme + theme(legend.key.width = unit(65, "mm"), legend.key.height = unit(5, "mm"))
)
) +
scale_seq_colour_manual(values = seq_palette) +
coord_chord() +
doc_theme
p_points
Point positions are integer midpoints of real bundled HSPs; identity
remains the reported pident. Lines use
qpos/spos, not interval ends, and an arrow points from
query to subject. The line default avoidance is none;
ribbon avoidance is uniform. Global chord rotation belongs
to the coordinate system. Pair selection and adjacent-row defaults in
linear plots are covered in three
modes.
The point-link colour scale supplies
available_aes = "link_colour" to its colourbar explicitly.
Ordinary ggplot2 colourbars and the wrapper default do not accept every
extended role; choose the matching role when making a custom guide.
Inspect each report and member rows before reading the plot. Aggregated bands are not new alignments; point links summarize midpoints of existing HSPs.
Combine original FASTA, GFF3 and standard BLAST tables into a traceable comparison. Inspect inputs, choose a display subset, then compare chord and full-length views.
In a fresh R session, run the code below, then follow this page in
order. Colours and layout are optional display choices; inputs select
only the fields needed. For file-based examples, first extract the input
bundle so the working directory contains examples/.
# Optional appearance choices; none of these columns or scales is required input.
library(ggchord)
seq_palette <- setNames(
c("#E64B35", "#233E4A", "#269B82", "#D59B17"),
c("MT108731.1", "MT118296.1", "OQ646790.1", "OR222515.1")
)
gene_palette <- c("+" = "#CE1536", "-" = "#346B9D", "." = "#8291A6")
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}This complete workflow uses four sequence records already present in
the source repository. It requires the matching development checkout or
the tutorial input bundle, not new alignments or external gene
prediction. Download
the inputs and unpack them so examples/ is under the
working directory. Record the package version and build
manifest with the exported figure.
library(dplyr)
sequence_source <- read_fasta_lengths(files = "examples/fasta/*.fna", source_file = TRUE)
seq_real <- sequence_source |>
select(accver, length)
annotation_source <- read_gff3(files = "examples/gff3/*.gff3", unstranded = "keep", source_file = TRUE)
feature_real <- annotation_source |>
select(accver, start, end, strand, type, anno)
alignment_source <- read_blast(files = "examples/blastn-standard/*.tsv", source_file = TRUE)
link_real <- alignment_source |>
select(qaccver, saccver, qstart, qend, sstart, send, pident, length)
real_validation <- validate_ggchord_data(seq_real, link_real)
real_validationThe FASTA headers supply exact accession versions and lengths. GFF3
contributes CDS intervals and their original strands. BLAST output
contributes existing alignments; the reader does not run BLAST. Inspect
real_validation before continuing: decreasing link
endpoints can be legitimate reverse-direction hits. Never turn unknown
annotation strands into + to make a gene shortcut accept
them.
real_prepared <- prepare_ggchord_data(seq_real, link_real,
features = list(annotation = feature_real)
)
real_filtered <- filter_ggchord_ribbons(link_real, min_pident = 80, min_length = 500)
# Explicit display subset: the six longest CDS per sequence; raw annotations stay intact.
real_display_features <- feature_real |>
group_by(accver) |>
slice_max(end - start + 1, n = 6, with_ties = FALSE) |>
ungroup()
real_labels <- real_display_features |>
filter(anno != "hypothetical protein") |>
group_by(accver) |>
slice_head(n = 2) |>
ungroup()
real_filtered$report| Metric | Observed output |
|---|---|
| Sequences | 4 |
| Annotation rows | 266 |
| CDS selected for the chord view | 24 |
| Original HSPs | 32 |
| HSPs retained by the display filter | 8 |
| Selected text labels | 6 |
The link display retains identity ≥80% and reported alignment length
≥500 bp. The chord view selects the six longest imported CDS per
sequence, labelling at most two selected CDS with informative
annotations (excluding hypothetical protein). These are display rules,
not biological findings or a claim that omitted genes are absent. Raw
feature_real and link_real remain intact;
real_prepared$report and real_filtered$report
retain preparation/filter evidence. No display strands are recoded. This
differs from the small packaged teaching fixture whose documented
generation includes display-strand recoding.
p_real_chord <- ggchord(seq_data = seq_real, ribbon_data = real_filtered$data, validate = "none") +
geom_link_ribbon(aes(ribbon_fill = pident), alpha = .65) +
geom_seq(show.legend = FALSE) +
geom_feature(aes(feature_fill = strand),
data = real_display_features,
width = .045, position = position_strand(.08)
) +
geom_feature_label_repel(
data = real_labels, label_layout = "radial",
label_wrap = 20, size = 4, position = position_strand(.08)
) +
geom_seq_label(size = 4, seq_label_radius = .84, seq_label_hjust = .5) +
coord_chord() +
scale_seq_colour_manual(values = seq_palette) +
scale_feature_fill_manual(values = gene_palette, breaks = c("+", "-")) +
scale_ribbon_fill_gradientn(
colours = identity_palette, values = identity_values,
limits = c(0, 100), breaks = identity_breaks, name = "Identity (%)", guide = identity_guide
) +
doc_theme
p_real_chord
Read sequence labels first, then source-strand feature arrows and interval links. Ribbons encode existing aligned intervals and mapped identity; their crossing is not itself evidence of a rearrangement. The selected features remain visibly separate from the much wider alignment spans. Use the full input tables when scientific interpretation needs annotations outside this display subset.
p_real_linear <- ggchord(seq_real, real_filtered$data, validate = "none") +
geom_link_ribbon(fill = "#6BAFA6", alpha = .45) +
geom_seq() +
geom_seq_label(size = 4) +
geom_feature(data = feature_real, feature_fill = "#CE1536", show.legend = FALSE, position = position_strand(.18)) +
geom_seq_scale(length = 10000) +
coord_collinear() +
doc_axis() +
scale_seq_colour_manual(values = seq_palette) +
doc_theme
p_real_linear
This view draws all imported annotations. Ribbons connect adjacent displayed sequences by default; retaining every comparison is an explicit layer decision. The bp scale bar belongs to collinear coordinates. See local views for cropping, source-coordinate axes and independent windows, and output to preview and save at the gallery’s stated dimensions.
The input archive contains unmodified FASTA/GFF3/BLAST files, a SHA256 manifest and the shared example script. The plot manifest records the code hash, PNG device, width, height and dpi. The accession names identify source records; a drawing is not a replacement for the original annotations or alignment evidence.
The *_source objects keep original fields and
provenance; seq_real has two columns,
feature_real only geometry/type/text, and
link_real adds identity and reported length only because
this workflow filters and colours by them. Standard 12-column BLAST
conversions are used, not a requirement for the historical 17 fields.
The bundled gene fixture preserves its original strand in
source_strand; the short examples restore that biological
strand before selecting four gene columns. See alignment input for exact format and unit
boundaries.
Check that IDs agree across the tables, inspect filtering reports and verify that guides encode identity and strand. Use the overview to locate regions, then focus a neighbourhood to study a CDS.
This is the end of Module I. Begin with tables, import, layer construction and geometry/clearance. 18 selected real CDS each receive repel labels; the complete 266-row annotation source remains separate. The four backbones combine zero, positive and negative curvature, varying radii and optimized display directions.
Extract the tutorial
input bundle into the working directory, so it contains
examples/. Install the optional example packages with
install.packages(c("dplyr", "purrr", "tidyr")); they
prepare tables, map lists and inspect endpoint columns.
# Optional appearance choices; none of these columns or scales is required input.
library(ggchord)
seq_palette <- setNames(
c("#E64B35", "#233E4A", "#269B82", "#D59B17"),
c("MT108731.1", "MT118296.1", "OQ646790.1", "OR222515.1")
)
gene_palette <- c("+" = "#CE1536", "-" = "#346B9D", "." = "#8291A6")
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}library(ggchord)
library(dplyr)
library(purrr)
library(tidyr)
# Original repository records; extract the tutorial input bundle first.
show_sequences <- read_fasta_lengths(files = "examples/fasta/*.fna") |>
select(accver, length)
show_annotations <- read_gff3(files = "examples/gff3/*.gff3", unstranded = "keep")
show_genes <- show_annotations |>
filter(type == "CDS") |>
select(accver, start, end, strand, anno)
show_alignments <- read_blast(files = "examples/blastn-standard/*.tsv")
# At most three substantial observed HSPs per pair; no coordinates are invented.
show_links <- show_alignments |>
filter(length >= 900, pident >= 75) |>
group_by(qaccver, saccver) |>
slice_max(length, n = 3, with_ties = FALSE) |>
ungroup()
# Select up to four source CDS per sequence, spread across annotation order.
# No feature is moved or invented; keep complete source tables separately.
show_features <- show_genes |>
filter(end - start + 1 >= 500) |>
group_by(accver) |>
arrange(start, .by_group = TRUE) |>
mutate(display_bin = ntile(row_number(), 4), informative = !is.na(anno) & anno != "hypothetical protein") |>
group_by(accver, display_bin) |>
arrange(desc(informative), desc(end - start), .by_group = TRUE) |>
slice_head(n = 1) |>
ungroup() |>
select(accver, start, end, strand, anno)
# Also retain well-separated negative-strand CDS, if not already represented.
show_reverse <- show_genes |>
filter(strand == "-", end - start + 1 >= 500) |>
group_by(accver) |>
arrange(start, .by_group = TRUE) |>
slice(c(1, n())) |>
ungroup()
show_features <- bind_rows(show_features, show_reverse) |>
distinct(accver, start, end, strand, .keep_all = TRUE) |>
arrange(accver, start)
show_labels <- show_features
show_optimization <- optimize_ggchord_layout(
show_sequences, show_links,
optimize = "orientation", weight = "length"
)The input contains four unchanged FASTA records, their GFF3 annotations and 32 original HSPs. The display keeps HSPs at least 900 bp long and at least 75% identical, with at most three per sequence pair. This reduces clutter without merging or inventing alignments.
show_curvature <- c(
"MT108731.1" = 0, "MT118296.1" = 0.85,
"OQ646790.1" = 1.1, "OR222515.1" = -0.4
)
show_radius <- c(
"MT108731.1" = 1, "MT118296.1" = 1.05,
"OQ646790.1" = 0.95, "OR222515.1" = 1.08
)
p_show_chord <- ggchord(show_sequences, show_links, show_features) +
geom_link_ribbon(aes(ribbon_fill = pident), link_avoid = "smooth", alpha = 0.58, linewidth = 0.18) +
geom_seq(
seq_orientation = show_optimization$seq_orientation,
seq_curvature = show_curvature, seq_radius = show_radius,
seq_gap = 0.06, linewidth = 1.1
) +
geom_gene(position = position_strand(0.07), width = 0.035) +
geom_gene_label_repel(
data = show_labels, label_layout = "radial", label_wrap = 24,
size = 3.1, label_max_lines = 3, position = position_strand(0.07)
) +
geom_seq_label(size = 4, seq_label_radius = 0.84, seq_label_hjust = 0.5) +
scale_seq_colour_manual(
values = seq_palette, name = "Sequence",
guide = sequence_guide
) +
scale_gene_fill_manual(values = gene_palette, breaks = c("+", "-"), name = "Strand") +
scale_ribbon_fill_gradientn(
colours = identity_palette, values = identity_values,
limits = c(0, 100), breaks = identity_breaks, name = "Identity (%)", guide = identity_guide
) +
guides(gene_fill = strand_guide) +
scale_seq_position_continuous(breaks = c(0, 20000, 40000), minor_breaks = NULL) +
coord_chord(rotation = 50) +
doc_theme +
labs(
title = "Four genomes, one connected view",
subtitle = "Signed curvature and radius · selected strand-aware CDS · local ribbon clearance"
)
p_show_chordgeom_seq(seq_curvature = ...) applies named values to
stable sequence identifiers. 0 is a straight backbone.
Curvature changes the display geometry; it does not alter sequence
lengths, biological strands or alignment endpoints.
Start with the sequence colours and gene directions, then trace the larger aligned intervals. A ribbon crossing is not evidence of a rearrangement by itself. The colour bar uses the same identity scale as the collinear view.
Use link filtering to change the display threshold, labels to choose annotation density, or sequence parameters to change radius and curvature. For a file-by-file introduction, start with original sequence comparisons.
Collinearity extends ggchord’s original chord grammar into a linear view. The design goal is to retain shared data and layers, switching coordinates wherever the contracts permit.
| Input | Required columns | Linear interpretation |
|---|---|---|
| seq_data | accver, length | One sequence per row with a shared bp x scale |
| ribbon_data | qaccver, saccver, qstart, qend, sstart, send | An observed interval correspondence between rows |
| gene_data | accver, start, end, strand | Genes separated above/below rows by a Position |
Optional fields and the historical BLAST naming follow 1.1. Identity needs pident; labels need anno. Source fields preserve coordinates in local windows; display gaps do not represent invented DNA lengths.
Reuse the FASTA/GFF3/BLAST workflow in 1.2, or import PAF, GRanges and GInteractions through adapters. Protein alignments require explicit CDS projection; aa endpoints must not be presented as bp.
The next page demonstrates replacing coord alone. Explicit circular radius, curvature, ring and radial sequence-label placement are rejected by the current linear contract rather than silently ignored. Keep coordinate-specific styling in the appropriate showcase. Perfect conversion of every possible composition remains a design goal, not a current guarantee.
Linear views default to adjacent displayed rows. Set
adjacent_only = FALSE in shared layers to retain the chord
link set. Choose manual sequence directions or coordinate-owned
automatic orientation, not both at once.
Verify the shared grammar before tuning linear composition. These objects replace only the coordinates on the chord teaching objects: no table edits or new alignments.
# Optional appearance choices; none of these columns or scales is required input.
library(ggchord)
seq_palette <- setNames(
c("#E64B35", "#233E4A", "#269B82", "#D59B17"),
c("MT108731.1", "MT118296.1", "OQ646790.1", "OR222515.1")
)
gene_palette <- c("+" = "#CE1536", "-" = "#346B9D", "." = "#8291A6")
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}
library(ggchord)
library(dplyr)
library(purrr)
library(tidyr)
# Original repository records; extract the tutorial input bundle first.
show_sequences <- read_fasta_lengths(files = "examples/fasta/*.fna") |>
select(accver, length)
show_annotations <- read_gff3(files = "examples/gff3/*.gff3", unstranded = "keep")
show_genes <- show_annotations |>
filter(type == "CDS") |>
select(accver, start, end, strand, anno)
show_alignments <- read_blast(files = "examples/blastn-standard/*.tsv")
# At most three substantial observed HSPs per pair; no coordinates are invented.
show_links <- show_alignments |>
filter(length >= 900, pident >= 75) |>
group_by(qaccver, saccver) |>
slice_max(length, n = 3, with_ties = FALSE) |>
ungroup()
# Select up to four source CDS per sequence, spread across annotation order.
# No feature is moved or invented; keep complete source tables separately.
show_features <- show_genes |>
filter(end - start + 1 >= 500) |>
group_by(accver) |>
arrange(start, .by_group = TRUE) |>
mutate(display_bin = ntile(row_number(), 4), informative = !is.na(anno) & anno != "hypothetical protein") |>
group_by(accver, display_bin) |>
arrange(desc(informative), desc(end - start), .by_group = TRUE) |>
slice_head(n = 1) |>
ungroup() |>
select(accver, start, end, strand, anno)
# Also retain well-separated negative-strand CDS, if not already represented.
show_reverse <- show_genes |>
filter(strand == "-", end - start + 1 >= 500) |>
group_by(accver) |>
arrange(start, .by_group = TRUE) |>
slice(c(1, n())) |>
ungroup()
show_features <- bind_rows(show_features, show_reverse) |>
distinct(accver, start, end, strand, .keep_all = TRUE) |>
arrange(accver, start)
show_labels <- show_features
show_optimization <- optimize_ggchord_layout(
show_sequences, show_links,
optimize = "orientation", weight = "length"
)p_learn_seq <- ggchord(seq_data = show_sequences) +
geom_seq() +
geom_seq_label(size = 4) +
scale_seq_colour_manual(
values = seq_palette, name = "Sequence",
guide = sequence_guide
) +
scale_seq_position_continuous(breaks = c(0, 20000, 40000), minor_breaks = NULL) +
coord_chord() +
doc_theme
p_learn_seq
p_learn_links <- p_learn_seq +
geom_link_ribbon(
aes(ribbon_fill = pident),
data = show_links,
adjacent_only = FALSE, link_avoid = "smooth"
) +
scale_ribbon_fill_gradientn(
colours = identity_palette, values = identity_values, limits = c(0, 100),
breaks = identity_breaks, name = "Identity (%)", guide = identity_guide
)
p_learn_links
p_learn_genes <- p_learn_links +
geom_gene(data = show_features, position = position_strand(0.08)) +
scale_gene_fill_manual(values = gene_palette, breaks = c("+", "-"), name = "Strand") +
guides(gene_fill = strand_guide)
p_learn_genes
p_learn_labels <- p_learn_genes +
geom_gene_label_repel(
data = show_features, label_layout = "arc", label_wrap = 24,
label_max_lines = 3, position = position_strand(0.08), size = 3
)
p_learn_labels
Rows share one bp scale. Different sequence lengths remain different rather than being stretched to equal width.
The same observed HSPs and cross-row connections remain. This also exposes the limitations of default long routes, addressed on the next page.
Arrows and labels use the same selected table. Display flips do not rewrite source strands. Continue to 2.3 Route cross-row links.
Adjacent and cross-row links impose different reading costs. Separate their data and controls while preserving observed HSP endpoints. Control points change display paths only.
# Optional appearance choices; none of these columns or scales is required input.
library(ggchord)
seq_palette <- setNames(
c("#E64B35", "#233E4A", "#269B82", "#D59B17"),
c("MT108731.1", "MT118296.1", "OQ646790.1", "OR222515.1")
)
gene_palette <- c("+" = "#CE1536", "-" = "#346B9D", "." = "#8291A6")
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}
library(ggchord)
library(dplyr)
library(purrr)
library(tidyr)
# Original repository records; extract the tutorial input bundle first.
show_sequences <- read_fasta_lengths(files = "examples/fasta/*.fna") |>
select(accver, length)
show_annotations <- read_gff3(files = "examples/gff3/*.gff3", unstranded = "keep")
show_genes <- show_annotations |>
filter(type == "CDS") |>
select(accver, start, end, strand, anno)
show_alignments <- read_blast(files = "examples/blastn-standard/*.tsv")
# At most three substantial observed HSPs per pair; no coordinates are invented.
show_links <- show_alignments |>
filter(length >= 900, pident >= 75) |>
group_by(qaccver, saccver) |>
slice_max(length, n = 3, with_ties = FALSE) |>
ungroup()
# Select up to four source CDS per sequence, spread across annotation order.
# No feature is moved or invented; keep complete source tables separately.
show_features <- show_genes |>
filter(end - start + 1 >= 500) |>
group_by(accver) |>
arrange(start, .by_group = TRUE) |>
mutate(display_bin = ntile(row_number(), 4), informative = !is.na(anno) & anno != "hypothetical protein") |>
group_by(accver, display_bin) |>
arrange(desc(informative), desc(end - start), .by_group = TRUE) |>
slice_head(n = 1) |>
ungroup() |>
select(accver, start, end, strand, anno)
# Also retain well-separated negative-strand CDS, if not already represented.
show_reverse <- show_genes |>
filter(strand == "-", end - start + 1 >= 500) |>
group_by(accver) |>
arrange(start, .by_group = TRUE) |>
slice(c(1, n())) |>
ungroup()
show_features <- bind_rows(show_features, show_reverse) |>
distinct(accver, start, end, strand, .keep_all = TRUE) |>
arrange(accver, start)
show_labels <- show_features
show_optimization <- optimize_ggchord_layout(
show_sequences, show_links,
optimize = "orientation", weight = "length"
)# Omit two short, crowded candidates from this linear display only.
# Both source rows remain in show_features and in the chord display.
show_linear_features <- show_features |>
filter(!anno %in% c("tail assembly protein", "gp4 protein"))
# Separate adjacent and cross-row links, retaining original endpoint values.
show_pair_rows <- show_links |>
mutate(
query_row = match(qaccver, show_sequences$accver),
subject_row = match(saccver, show_sequences$accver),
span = abs(query_row - subject_row)
)
show_adjacent <- show_pair_rows |> filter(span == 1)
# One longest observed HSP per non-neighbour pair keeps the long routes legible.
show_cross <- show_pair_rows |>
filter(span > 1) |>
group_by(qaccver, saccver) |>
slice_max(length, n = 1, with_ties = FALSE) |>
ungroup()
# Inspect a backbone-only layout using the same alignment and gap as the final view.
show_linear_coord <- coord_collinear(align = "center", orientation = "auto", ratio = 0.7)
show_routing_base <- ggchord(show_sequences, show_links) +
geom_seq(seq_gap = 0.42) + show_linear_coord
show_reference <- get_chord_layout(show_routing_base)$sequence_reference
# Shared controls sit just beyond the intervening backbones, rather than far away.
show_cross_controls <- map2(show_cross$query_row, show_cross$subject_row, function(query_row, subject_row) {
between <- seq(min(query_row, subject_row) + 1L, max(query_row, subject_row) - 1L)
middle_ids <- show_sequences$accver[between]
right_edge <- max(show_reference$x_offset[middle_ids] + show_reference$lens[middle_ids] * show_reference$x_scale)
middle_y <- mean(show_reference$y[show_sequences$accver[c(query_row, subject_row)]])
c(right_edge + if (abs(query_row - subject_row) == 3) 0.85 else 0.65, middle_y)
})
p_show_collinear <- ggchord(show_sequences, gene_data = show_linear_features) +
geom_link_ribbon(
aes(ribbon_fill = pident),
data = show_adjacent,
link_avoid = "smooth", alpha = 0.48, linewidth = 0.22
) +
geom_link_ribbon(
aes(ribbon_fill = pident),
data = show_cross,
adjacent_only = FALSE, link_ctrl_point = show_cross_controls,
link_avoid = "smooth", alpha = 0.28, linewidth = 0.3
) +
geom_seq(seq_gap = 0.42, linewidth = 1) +
geom_gene(position = position_strand(0.09), width = 0.045) +
geom_gene_label_repel(
data = show_linear_features |> arrange(desc(nchar(anno)), accver, start),
label_layout = "auto", label_wrap = 24,
label_side = "auto", position = position_strand(0.09), size = 3.3
) +
geom_seq_label(size = 4) +
geom_seq_scale(length = 10000, offset = unit(c(0, 10), "mm")) +
scale_seq_colour_manual(
values = seq_palette, name = "Sequence",
guide = sequence_guide
) +
scale_gene_fill_manual(values = gene_palette, breaks = c("+", "-"), name = "Strand") +
scale_ribbon_fill_gradientn(
colours = identity_palette, values = identity_values,
limits = c(0, 100), breaks = identity_breaks, name = "Identity (%)", guide = identity_guide
) +
guides(gene_fill = strand_guide) +
scale_seq_position_continuous(breaks = c(0, 20000, 40000, 60000, 80000), minor_breaks = NULL) +
show_linear_coord +
doc_theme +
labs(
title = "Sequence architecture, across four genomes",
subtitle = "Selected labelled CDS · strand-separated arrows · routed cross-row comparisons"
)
p_show_collinear
The linear showcase omits the crowded tail assembly protein and gp4
protein candidates, retaining them in the source and chord view; the
remaining 16 features are individually labelled. Keep the selected
adjacent HSPs and one longest HSP per non-neighbour pair.
show_cross_controls matches show_cross row
order; rebuild both after sorting/filtering. Controls use exported
layout x/y units, not bp. Each link accepts two or four numbers, shaping
its two ribbon edges rather than invoking a global route planner.
Shorter sequences are horizontally centred. A backbone-only layout provides actual row positions, lengths and offsets; controls are placed just beyond the intervening right ends rather than far from the data. Row spacing uses seq_gap and the longest backbone width, while controls do not accept bp coordinates. Changing spacing, order or orientation requires inspecting the paths again; arbitrary controls are not guaranteed free of self-intersection.
position_strand() separates strands.
link_avoid = "smooth" with the default NULL gap retreats
locally near arrows. Non-adjacent outlines are dashed by default; a
larger linewidth makes them visible without overriding linetype.
Adjacency follows display order, not biological alignment direction.
geom_seq_scale() defaults to a 2 mm horizontal and 6 mm
vertical offset. The earlier inset was configuration, not incorrect bp
conversion. offset = unit(c(0, 10), "mm") aligns the start
with the left sequence envelope and reserves vertical label space. With
centred rows, source coordinate0 is no longer aligned across sequences;
the ruler uses the left envelope, and reversed rows have their source
origin at the other end. A scale bar measures distance rather than a
particular source interval.
Use focus/segments for local questions and CDS neighbourhoods for homology evidence; review the 2.6 Showcase. GC remains available in tracks, while the flagship reserves space for labels and links.
Compare AmpR neighbourhoods in two plasmids. Check real coding DNA, establish correspondence, then focus to each CDS plus 200 bp on either side.
Only bundled data is needed; no download or new alignment. Load the package and optional display settings first.
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}Select AmpR annotations from bundled pUC19 and pETDuet-1, retaining IDs, coordinates, strand and text. Reverse-complement negative-strand DNA so comparisons use the same biological orientation rather than annotation names alone.
library(dplyr)
data(plasmid_example_pUC19)
data(plasmid_example_pETDuet_1)
homolog_source <- bind_rows(plasmid_example_pUC19, plasmid_example_pETDuet_1)
homolog_seq <- homolog_source |>
select(accver, length)
cluster_features <- homolog_source |>
find_common_features() |>
filter(type == "CDS", anno == "AmpR") |>
select(accver, start, end, strand, anno) |>
arrange(match(accver, homolog_source$accver)) |>
mutate(feature_id = paste(accver, start, end, sep = ":"))
# Membership is computed from exact strand-corrected CDS DNA equality, not gene names.
reverse_complement <- function(sequence) {
bases <- strsplit(sequence, "")[[1]]
chartr("ACGT", "TGCA", paste(rev(bases), collapse = ""))
}
coding_sequences <- cluster_features |>
left_join(select(homolog_source, accver, sequence), by = "accver") |>
rowwise() |>
mutate(
coding_dna = substr(sequence, start, end),
coding_dna = if (strand == "-") reverse_complement(coding_dna) else coding_dna
) |>
ungroup()
cds_dna <- pull(coding_sequences, coding_dna)cluster_features should contain two actual CDS;
cds_dna retains their oriented coding sequences. This
strict example checks exact DNA equality. General homology analysis can
use external alignments and clustering; homologs need not be
identical.
Build membership from exact coding-DNA equality. Different DNA does not enter one group merely because both annotations say AmpR.
membership <- tibble(
feature_id = cluster_features$feature_id,
cluster_id = paste0("exact_CDS_", match(cds_dna, unique(cds_dna)))
)
homologs <- link_ggchord_clusters(cluster_features, membership,
pairs = tibble(qaccver = homolog_seq$accver[1], saccver = homolog_seq$accver[2])
)
loci <- find_ggchord_loci(homolog_seq, cluster_features,
targets = cluster_features$feature_id[cluster_features$anno == "AmpR"][1], upstream = 100, downstream = 100
)Inspect membership and
homologs$report$comparisons and $members. The
linking utility consumes existing groups; it does not compute a new
alignment. Unknown members and ambiguous paralogs are reported; only
explicit paralogs = "all" requests all combinations within
the documented bounds.
Full plasmid lengths would shrink the individual CDS. Construct windows from each real CDS source interval and add 200 bp on either side, retaining the original sequence and annotation tables.
# Compare equivalent local windows instead of shrinking one gene on full plasmids.
homolog_windows <- cluster_features |>
select(accver, start, end)
local_homologs <- focus_ggchord_data(homolog_seq, homologs$data, cluster_features,
loci = homolog_windows, expand = 200
)
p_homologs <- ggchord(
seq_data = local_homologs$seq_data,
ribbon_data = local_homologs$ribbon_data, gene_data = local_homologs$gene_data
) +
geom_link_ribbon(fill = "#269B82", alpha = .65) +
geom_seq(colour = "#233E4A", show.legend = FALSE) +
geom_feature(
data = local_homologs$gene_data, feature_fill = "#F1D334",
position = position_strand(.10), width = .07
) +
geom_feature_label_repel(data = local_homologs$gene_data, size = 4, label_fit = "shrink") +
geom_seq_label(size = 4) +
geom_seq_break() +
coord_collinear() +
doc_axis("source") +
doc_theme
p_homologs
Gold arrows identify the checked identical coding sequences; the purple band connects their CDS ranges and internal text names AmpR. Axes use each original sequence’s positions, so one horizontal location can have different source labels. Break markers indicate that only local windows remain.
local_homologs$report records retention and clipping.
Genes outside the windows are omitted, not absent. The band represents
declared interval correspondence, not a base alignment; envelopes of
segmented annotations likewise do not establish exon-level
correspondence.
# Exact CDS DNA equality supplies evidence for these selected ungapped residues.
matching <- homologs$data |>
slice_head(n = 1)
paired_coding <- cluster_features |>
mutate(phase = 0)
paired_cds <- build_ggchord_cds(paired_coding)
protein_link <- tibble(
qfeature_id = matching$qfeature_id, sfeature_id = matching$sfeature_id,
qstart = 1, qend = 20, sstart = 1, send = 20
)
projected_links <- project_ggchord_links(protein_link, paired_cds, ungapped = TRUE)
projected_links| Metric | Observed output |
|---|---|
| Projected genomic feature rows | 1 |
| Projected nucleotide link rows | 1 |
The first 20 residues of identical CDS provide sequence evidence for
ungapped = TRUE. Equal spans alone do not justify that
claim for ordinary BLASTp HSPs. The utility retains protein statistics
rather than presenting them as nucleotide identity.
Both internal labels should read AmpR and arrows retain the actual negative strand. Coding DNA supports correspondence; source axes and focus reports explain the local display. Save at explicit 10 × 5 in dimensions.
Next: read real BLASTp query intervals, understand CDS projection and segments, or create independent local windows.
Select local windows or segments while keeping source positions distinct from display transformations.
In a fresh R session, run the code below, then follow this page in order. Colours and layout are optional display choices; inputs select only the fields needed.
library(ggchord)
library(dplyr)
data(seq_data_example)
data(ribbon_data_example)
data(gene_data_example)
seqs <- seq_data_example |>
select(accver, length)
links <- ribbon_data_example |>
select(qaccver, saccver, qstart, qend, sstart, send)
# The fixture preserves biological strands separately from its old display strands.
genes <- gene_data_example |>
transmute(accver, start, end, strand = source_strand)
# Optional appearance choices; none of these columns or scales is required input.
library(ggchord)
seq_palette <- setNames(
c("#E64B35", "#233E4A", "#269B82", "#D59B17"),
c("MT108731.1", "MT118296.1", "OQ646790.1", "OR222515.1")
)
gene_palette <- c("+" = "#CE1536", "-" = "#346B9D", "." = "#8291A6")
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}After this page’s preparation code:
focus_loci <- tibble(
accver = seqs$accver,
start = floor(seqs$length * .25), end = floor(seqs$length * .75)
)
focused <- focus_ggchord_data(seqs, links, genes, loci = focus_loci)
p_focus <- ggchord(
seq_data = focused$seq_data, ribbon_data = focused$ribbon_data,
gene_data = focused$gene_data
) +
geom_seq() +
geom_link_ribbon(fill = "#6BAFA6", alpha = .5) +
geom_gene() +
geom_seq_break() +
geom_seq_label(size = 4) +
scale_seq_colour_manual(values = seq_palette) +
scale_gene_fill_manual(values = gene_palette) +
doc_axis("source") +
coord_collinear() +
doc_theme
p_focus
Focus relocates retained positions to local one-based coordinates and
retains source origins, truncation flags and a report.
boundary = "trim" can clip links by a linear display
projection; it does not realign sequences. A scaled alignment length is
flagged as estimated. coordinates = "source" restores
source axis labels, including after repeated focusing.
windows <- tibble(
accver = rep(seqs$accver[1], 2),
start = c(1, floor(seqs$length[1] * .65)), end = c(floor(seqs$length[1] * .4), seqs$length[1]),
locus_id = c("left_window", "right_window"), row_id = seqs$accver[1], orientation = c(1, -1)
)
segmented <- segment_ggchord_data(slice_head(seqs, n = 1), windows = windows)
p_segments <- ggchord(seq_data = segmented$seq_data) +
geom_seq() +
coord_collinear(segments = segmented$segments, segment_gap = 2000)
p_segmentsEach window has a reversible source mapping; omitted gaps have no
mapping. map_ggchord_position() maps explicit source
positions into a plot for ordinary ggplot2 annotations. Supply the
window ID when windows share a source sequence. Do not interpret display
orientation as a change to the original strand.
find_ggchord_loci() selects declared feature/name
queries. link_ggchord_clusters() requires an explicit
membership table and sequence pairs; equal gene names do not establish
homology. filter_ggchord_ribbons(),
deduplicate_ggchord_ribbons() and
merge_ggchord_ribbons() prepare existing alignments,
preserving source membership and estimated-length flags. Bundle and
density statistics summarize provided links rather than creating new
sequence alignments.
source_position <- focus_loci$start[1] + 100
source_mark <- map_ggchord_position(p_focus, seqs$accver[1], source_position, source = TRUE)
p_marked <- p_focus +
ggplot2::geom_point(
data = source_mark, aes(x, y),
inherit.aes = FALSE, colour = "#D59B17", size = 3
)
p_markedRun the focus example first. The helper returns Cartesian x/y for a source position 100 bp inside the first selected window. It does not make all ordinary ggplot2 coordinates become genomic bp. In segmented views choose the window ID, and check positions in omitted gaps rather than extrapolating across them. Use explicit homologs when the locus selection comes from declared feature/cluster evidence.
Inspect cropping reports, break markers and source coordinates. Omitted gaps have no mapping; a display flip alone is not evidence of inversion.
Collinearity extends the chord coordinate grammar; reusing data and layers is the goal. Read inputs, coordinate replacement and routes/ruler, then run the complete example. The identity scale and feature candidates are shared with the chord example; this linear composition omits two crowded short candidates, displaying 16 CDS with labels, with positive and negative strands above/below backbones.
Extract the input bundle and install dplyr, purrr and tidyr first:
# Optional appearance choices; none of these columns or scales is required input.
library(ggchord)
seq_palette <- setNames(
c("#E64B35", "#233E4A", "#269B82", "#D59B17"),
c("MT108731.1", "MT118296.1", "OQ646790.1", "OR222515.1")
)
gene_palette <- c("+" = "#CE1536", "-" = "#346B9D", "." = "#8291A6")
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}library(ggchord)
library(dplyr)
library(purrr)
library(tidyr)
# Original repository records; extract the tutorial input bundle first.
show_sequences <- read_fasta_lengths(files = "examples/fasta/*.fna") |>
select(accver, length)
show_annotations <- read_gff3(files = "examples/gff3/*.gff3", unstranded = "keep")
show_genes <- show_annotations |>
filter(type == "CDS") |>
select(accver, start, end, strand, anno)
show_alignments <- read_blast(files = "examples/blastn-standard/*.tsv")
# At most three substantial observed HSPs per pair; no coordinates are invented.
show_links <- show_alignments |>
filter(length >= 900, pident >= 75) |>
group_by(qaccver, saccver) |>
slice_max(length, n = 3, with_ties = FALSE) |>
ungroup()
# Select up to four source CDS per sequence, spread across annotation order.
# No feature is moved or invented; keep complete source tables separately.
show_features <- show_genes |>
filter(end - start + 1 >= 500) |>
group_by(accver) |>
arrange(start, .by_group = TRUE) |>
mutate(display_bin = ntile(row_number(), 4), informative = !is.na(anno) & anno != "hypothetical protein") |>
group_by(accver, display_bin) |>
arrange(desc(informative), desc(end - start), .by_group = TRUE) |>
slice_head(n = 1) |>
ungroup() |>
select(accver, start, end, strand, anno)
# Also retain well-separated negative-strand CDS, if not already represented.
show_reverse <- show_genes |>
filter(strand == "-", end - start + 1 >= 500) |>
group_by(accver) |>
arrange(start, .by_group = TRUE) |>
slice(c(1, n())) |>
ungroup()
show_features <- bind_rows(show_features, show_reverse) |>
distinct(accver, start, end, strand, .keep_all = TRUE) |>
arrange(accver, start)
show_labels <- show_features
show_optimization <- optimize_ggchord_layout(
show_sequences, show_links,
optimize = "orientation", weight = "length"
)Retain four full FASTA records, 266 CDS and 32 original HSPs. Adjacent rows use the explicitly selected observations; each non-neighbour pair retains only its longest HSP to reduce overlap.
The flagship no longer includes GC tracks; composition analysis remains in tracks. This keeps two different reading tasks from competing in the entry figure.
# Omit two short, crowded candidates from this linear display only.
# Both source rows remain in show_features and in the chord display.
show_linear_features <- show_features |>
filter(!anno %in% c("tail assembly protein", "gp4 protein"))
# Separate adjacent and cross-row links, retaining original endpoint values.
show_pair_rows <- show_links |>
mutate(
query_row = match(qaccver, show_sequences$accver),
subject_row = match(saccver, show_sequences$accver),
span = abs(query_row - subject_row)
)
show_adjacent <- show_pair_rows |> filter(span == 1)
# One longest observed HSP per non-neighbour pair keeps the long routes legible.
show_cross <- show_pair_rows |>
filter(span > 1) |>
group_by(qaccver, saccver) |>
slice_max(length, n = 1, with_ties = FALSE) |>
ungroup()
# Inspect a backbone-only layout using the same alignment and gap as the final view.
show_linear_coord <- coord_collinear(align = "center", orientation = "auto", ratio = 0.7)
show_routing_base <- ggchord(show_sequences, show_links) +
geom_seq(seq_gap = 0.42) + show_linear_coord
show_reference <- get_chord_layout(show_routing_base)$sequence_reference
# Shared controls sit just beyond the intervening backbones, rather than far away.
show_cross_controls <- map2(show_cross$query_row, show_cross$subject_row, function(query_row, subject_row) {
between <- seq(min(query_row, subject_row) + 1L, max(query_row, subject_row) - 1L)
middle_ids <- show_sequences$accver[between]
right_edge <- max(show_reference$x_offset[middle_ids] + show_reference$lens[middle_ids] * show_reference$x_scale)
middle_y <- mean(show_reference$y[show_sequences$accver[c(query_row, subject_row)]])
c(right_edge + if (abs(query_row - subject_row) == 3) 0.85 else 0.65, middle_y)
})
p_show_collinear <- ggchord(show_sequences, gene_data = show_linear_features) +
geom_link_ribbon(
aes(ribbon_fill = pident),
data = show_adjacent,
link_avoid = "smooth", alpha = 0.48, linewidth = 0.22
) +
geom_link_ribbon(
aes(ribbon_fill = pident),
data = show_cross,
adjacent_only = FALSE, link_ctrl_point = show_cross_controls,
link_avoid = "smooth", alpha = 0.28, linewidth = 0.3
) +
geom_seq(seq_gap = 0.42, linewidth = 1) +
geom_gene(position = position_strand(0.09), width = 0.045) +
geom_gene_label_repel(
data = show_linear_features |> arrange(desc(nchar(anno)), accver, start),
label_layout = "auto", label_wrap = 24,
label_side = "auto", position = position_strand(0.09), size = 3.3
) +
geom_seq_label(size = 4) +
geom_seq_scale(length = 10000, offset = unit(c(0, 10), "mm")) +
scale_seq_colour_manual(
values = seq_palette, name = "Sequence",
guide = sequence_guide
) +
scale_gene_fill_manual(values = gene_palette, breaks = c("+", "-"), name = "Strand") +
scale_ribbon_fill_gradientn(
colours = identity_palette, values = identity_values,
limits = c(0, 100), breaks = identity_breaks, name = "Identity (%)", guide = identity_guide
) +
guides(gene_fill = strand_guide) +
scale_seq_position_continuous(breaks = c(0, 20000, 40000, 60000, 80000), minor_breaks = NULL) +
show_linear_coord +
doc_theme +
labs(
title = "Sequence architecture, across four genomes",
subtitle = "Selected labelled CDS · strand-separated arrows · routed cross-row comparisons"
)
p_show_collinearDashed outlines identify non-adjacent displayed rows, with sufficient linewidth to read. Right-side controls separate long routes; smooth local clearance retreats near arrows. Identity has the same left-side colourbar as the chord view, and the bottom strand keys use arrows. The ruler’s horizontal offset is 0; its length still uses the shared bp conversion.
Rebuild controls when changing seq_gap, order, orientation or link selection. Local arrow clearance is not an automatic route around intervening whole sequences; ribbons may still cross legitimately. Use focus/segments for local questions. Explicit circular radius/curvature cannot currently be passed to linear coordinates; see conversion boundaries.
A single-sequence circular map starts with two columns. Feature, primer and restriction searches additionally need real DNA; existing annotations can be drawn directly without running recognition again.
| Input | Required content | Optional content |
|---|---|---|
| Backbone | accver, length; exactly one sequence | label for display; sequence for searches |
| Features | accver, start, end and declared directionality | type, anno/label, feature_id and shape categories |
| Primers | Computed binding interval, strand/name roles required by the geom | Recognition and provenance fields |
| Restriction sites | Position/name roles required by the geom | Enzyme catalogue, methylation assumption and filter provenance |
Backbones do not require DNA. Prefer search-result tables for the latter layers and consult their help for detailed fields. Unknown-strand annotations belong in generic feature layers.
library(ggchord)
library(dplyr)
plasmid_example_pUC19 |> select(accver, length)
find_common_features(plasmid_example_pUC19) |> select(accver, start, end, strand, type, anno) |> slice_head(n = 5)The compact names retain ggchord’s early BLAST workflow history. accver need not be a database accession. Full DNA and length must agree. Recognition is computational matching, not experimental validation.
read_fasta_lengths() reads IDs and full lengths only.
Searches additionally need actual DNA, for example read with optional
Biostrings readDNAStringSet() and
as.character(), retaining matching IDs. GFF3/BED/GenBank or
GRanges annotations follow the format guide,
retaining provenance. Handle circular-origin and segmented records
according to the reader contract; do not invent one continuous interval
by merging every segment.
Focus on the visual information added at each step. Preparation computes inputs; independent layers still control drawing.
library(ggchord)
feature_palette <- c(
CDS = "#993366", promoter = "#31849B", rep_origin = "#F1E51D",
primer_bind = "#A020F0", misc_feature = "#A6ACB3", protein_bind = "#31849B"
)
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}
library(dplyr)
data(plasmid_example_pUC19)
plasmid_seq <- plasmid_example_pUC19 |>
select(accver, length)
plasmid_feature_source <- find_common_features(plasmid_example_pUC19)
# Dedicated primer layers already show bindings; avoid duplicate primer annotations.
plasmid_features <- plasmid_feature_source |>
filter(type != "primer_bind") |>
select(accver, start, end, strand, type, anno)
plasmid_primers <- find_primer_bindings(plasmid_example_pUC19, set = "universal")
plasmid_sites <- find_restriction_sites(plasmid_example_pUC19)
display_sites <- filter_restriction_sites(plasmid_sites, enzymes = c("EcoRI", "BamHI", "HindIII"))
primer_names <- primer_catalog(set = "universal")plasmid_position <- position_feature_stack(spacing = 0.085, base_position = position_plasmid())
p_plasmid_backbone <- ggchord(seq_data = plasmid_seq) +
geom_seq(seq_style = "double", colour = "#252525") +
geom_seq_center_label() +
coord_circular(rotation = 90) +
theme_ggchord_plasmid() +
theme(
panel.background = element_blank(),
plot.background = element_rect(fill = doc_background, colour = NA)
)
p_plasmid_features <- p_plasmid_backbone +
geom_feature_plasmid(data = plasmid_features, position = plasmid_position)
p_plasmid_text <- p_plasmid_features +
geom_feature_label_repel(
data = plasmid_features, position = plasmid_position,
external = TRUE, label_fit = "shrink", label_min_size = 2.5
)
p_plasmid_text
Check name and full length first. coord_circular()
accepts one sequence; the geom owns the double-line style.
position_feature_stack() separates overlaps;
geom_feature_plasmid() selects shapes and colours by real
category. Compact short-feature shapes do not imply missing data.
Geometry and labels share a Position. Allow shrinking only above an explicit minimum and external placement when necessary; recheck after changing dimensions.
plasmid_position <- position_feature_stack(spacing = .10, base_position = position_plasmid())
p_plasmid <- ggchord(seq_data = plasmid_seq) +
geom_seq(seq_style = "double") +
geom_feature_plasmid(aes(feature_fill = type), data = plasmid_features, position = plasmid_position) +
geom_feature_label_repel(
data = plasmid_features, position = plasmid_position,
size = 4
) +
geom_primer(data = plasmid_primers, colour = "#A020F0") +
geom_primer_label_repel(data = plasmid_primers, colour = "#A020F0", max_overlaps = 0, size = 3) +
geom_restriction_site(data = display_sites, colour = "#29364B", label_size = 3, leader = "radial") +
geom_seq_center_label() +
scale_feature_fill_manual(values = feature_palette) +
scale_seq_position_continuous() +
coord_circular(rotation = 90) +
theme_ggchord_plasmid() +
theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank()
)
p_plasmid
Only three selected enzyme sites are displayed; the full search table remains available. See 3.3 for recognition provenance and assumptions. Compare annotation densities across 13 references, then finish the dense pETDuet-1 showcase.
Search known DNA for primers, restriction sites and common features, then choose a display. Computational matches and experimental validation are different.
In a fresh R session, run the code below, then follow this page in order. Colours and layout are optional display choices; inputs select only the fields needed.
Use the bundled pUC19 DNA for exact catalogue-primer and selected restriction searches:
data(plasmid_example_pUC19)
target <- setNames(plasmid_example_pUC19$sequence, plasmid_example_pUC19$accver)
primers <- find_primer_bindings(target, set = "universal")
sites <- find_restriction_sites(target, enzymes = c("EcoRI", "BamHI", "HindIII"), methylation = "none")
recognition_seq <- plasmid_example_pUC19 |>
select(accver, length)
p_sites <- ggchord(seq_data = recognition_seq) +
geom_seq() +
geom_primer(data = primers, colour = "#A020F0") +
geom_restriction_site(data = sites, colour = "#29364B") +
coord_circular()
p_sitesSearch supports circular-origin binding; set
circular = FALSE for a linear sequence. The actual pUC19
hits shown here should be inspected in the returned table. Search hits
are computational matches, not experimental validation. Primer
"three_prime" mode finds an exact suffix, with no mismatch
model; its Wallace melting-temperature estimate is a composition
heuristic.
find_restriction_sites() returns unfiltered matches and
cleavage coordinates; filter_restriction_sites() selects a
display set. Enzyme site counts refer to the full supplied sequence and
remain stable after filtering. methylation = "auto" uses
audited context only for exactly recognized bundled references; it does
not claim unknown samples are unblocked. This example explicitly uses
"none"; this is a search assumption, not a measurement of
methylation in a physical sample.
geom_restriction_site(leader = "radial") is the default
route.
find_common_features() supports curated exact-reference
annotations, DNA and record-specific matching modes. Protein searches
use standard-code six-frame translation; they do not implement splicing,
alternative initiation or origin-crossing protein matches. Source
genetic-code metadata does not select another translation table. For
nonstandard codes use DNA matching or external code-aware analysis.
Confidence is a heuristic label, not a probability. Use explicit CDS projection for known protein/genome
maps.
This short workflow uses the same source-backed plasmid as the complete map. The complete pUC19 workflow demonstrates catalogue annotations, exact reference matching, site display filtering, primer labels and feature stacking together. Changing only a sequence name does not make it a recognized reference; sequence content determines source-backed recognition. Read the result methods and methylation provenance instead of assigning reference claims to an unknown sample.
Inspect match positions, methods and provenance. Restriction filtering is a display choice; methylation search conditions are not a measurement of an unknown sample.
This gallery contains all 13 unique repository FASTA reference records, redrawn with current code. Historical PNGs and .dna files are not these outputs. Vector roles provide learning prompts rather than experimental validation. Bundled records and their generation rules retain source identity and lengths.
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}library(ggchord)
library(dplyr)
library(purrr)
# Thirteen unique records; the compatibility spelling pUC19c is not an extra plasmid.
catalog_objects <- c(
"plasmid_example_pSB1C3", "plasmid_example_pUC19", "plasmid_example_pBR322",
"plasmid_example_pBluescript_II_SK_plus", "plasmid_example_pET_28a_plus",
"plasmid_example_pETDuet_1", "plasmid_example_pDONR221",
"plasmid_example_pcDNA3_1_plus", "plasmid_example_pTRE_Tight_BI",
"plasmid_example_pTRIPZ", "plasmid_example_pCAMBIA1300",
"plasmid_example_pEarleyGate_201", "plasmid_example_pSpCas9_BB_2A_GFP_PX458"
)
read_catalog_record <- function(object) {
target <- new.env(parent = emptyenv())
data(list = object, package = "ggchord", envir = target)
target[[object]]
}
catalog_sequences <- map(catalog_objects, read_catalog_record)
catalog_features <- map(catalog_sequences, find_common_features)
catalog_summary <- map2(catalog_sequences, catalog_features, function(sequence, features) {
tibble(accver = sequence$accver, length = sequence$length, features = nrow(features))
}) |>
bind_rows()make_catalog_plot <- function(sequence, features) {
placement <- position_feature_stack(spacing = 0.085, base_position = position_plasmid())
ggchord(seq_data = sequence |> select(accver, length)) +
geom_seq(seq_style = "double", colour = "#252525") +
geom_feature_plasmid(data = features, position = placement) +
geom_feature_label_repel(
data = features, position = placement, external = FALSE,
label_fit = "shrink", label_min_size = 2.5, max_overlaps = 0
) +
geom_seq_center_label() +
coord_circular(rotation = 90, fit = "manual", xlim = c(-1.35, 1.35), ylim = c(-1.35, 1.35)) +
theme_ggchord_plasmid() +
theme(
panel.background = element_blank(),
plot.background = element_rect(fill = doc_background, colour = NA)
)
}
catalog_plots <- map2(catalog_sequences, catalog_features, make_catalog_plot)
names(catalog_plots) <- catalog_summary$accver
# Save any record at the same size used in the gallery.
# ggsave("pBR322.png", catalog_plots[["pBR322"]], width = 10, height = 8, dpi = 144)One small function composes independent layers, retaining every recognition result in catalog_features. Actual counts appear below. These maps retain all feature geometry and fit labels inside, allowing labels to hide when space is insufficient; primers and dense restriction annotations are combined in the complete case. Save a named plot with the commented ggsave call.
| Reference | Length (bp) | Features |
|---|---|---|
| pSB1C3 | 2070 | 7 |
| pUC19 | 2686 | 9 |
| pBR322 | 4361 | 6 |
| pBluescript II SK(+) | 2961 | 14 |
| pET-28a(+) | 5369 | 16 |
| pETDuet-1 | 5420 | 20 |
| pDONR221 | 4761 | 11 |
| pcDNA3.1(+) | 5428 | 13 |
| pTRE-Tight-BI | 2856 | 15 |
| pTRIPZ | 13320 | 33 |
| pCAMBIA1300 | 8959 | 17 |
| pEarleyGate 201 | 11779 | 21 |
| pSpCas9(BB)-2A-GFP (PX458) | 9288 | 19 |
After comparing densities, continue to 3.5 The complete case, adding separate primer and restriction layers.
Build a readable plasmid map from bundled pUC19 DNA, with separate annotation, primer and restriction layers. Keep display choices separate from complete results.
In a fresh R session, run the code below, then follow this page in order. Colours and layout are optional display choices; inputs select only the fields needed.
library(ggchord)
feature_palette <- c(
CDS = "#993366", promoter = "#31849B", rep_origin = "#F1E51D",
primer_bind = "#A020F0", misc_feature = "#A6ACB3", protein_bind = "#31849B"
)
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}The preparation above declares the palette and shared appearance.
The bundled plasmid_example_pUC19 is copied from the
repository’s original examples/plasmid/pUC19.fna; bases and
sequence identity are not edited for display. The workflow uses the
package’s source-backed recognition catalogues. It needs no network
request or experimental measurements fabricated for the figure.
library(dplyr)
data(plasmid_example_pUC19)
plasmid_seq <- plasmid_example_pUC19 |>
select(accver, length)
plasmid_feature_source <- find_common_features(plasmid_example_pUC19)
# Dedicated primer layers already show bindings; avoid duplicate primer annotations.
plasmid_features <- plasmid_feature_source |>
filter(type != "primer_bind") |>
select(accver, start, end, strand, type, anno)
plasmid_primers <- find_primer_bindings(plasmid_example_pUC19, set = "universal")
plasmid_sites <- find_restriction_sites(plasmid_example_pUC19)
display_sites <- filter_restriction_sites(plasmid_sites, enzymes = c("EcoRI", "BamHI", "HindIII"))
primer_names <- primer_catalog(set = "universal")Inspect plasmid_features, plasmid_primers
and display_sites to distinguish complete search results
from display choices.
Add the backbone, annotations, labels, primers and restriction sites in that order. Features and their labels share one Position; each layer reads its own data.
plasmid_position <- position_feature_stack(spacing = .10, base_position = position_plasmid())
p_plasmid <- ggchord(seq_data = plasmid_seq) +
geom_seq(seq_style = "double") +
geom_feature_plasmid(aes(feature_fill = type), data = plasmid_features, position = plasmid_position) +
geom_feature_label_repel(
data = plasmid_features, position = plasmid_position,
size = 4
) +
geom_primer(data = plasmid_primers, colour = "#A020F0") +
geom_primer_label_repel(data = plasmid_primers, colour = "#A020F0", max_overlaps = 0, size = 3) +
geom_restriction_site(data = display_sites, colour = "#29364B", label_size = 3, leader = "radial") +
geom_seq_center_label() +
scale_feature_fill_manual(values = feature_palette) +
scale_seq_position_continuous() +
coord_circular(rotation = 90) +
theme_ggchord_plasmid() +
theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank()
)
p_plasmid
| Metric | Observed output |
|---|---|
| Full sequence length (bp) | 2686 |
| Annotation rows | 7 |
| Exact primer hit records | 6 |
| Enzyme match records (not distinct positions) | 49845 |
| Restriction records selected for display | 3 |
Mixed exterior annotations share root-ordered placement, including sparse restriction labels beside primers. This example uses the dedicated plasmid theme and default feature fitting: internal text is tried before adjacent/external placement. Inspect at the final export dimensions instead of hiding crossings by shrinking text or forcing offsets.
The original annotation table is retained in
plasmid_feature_source. Dedicated primer layers replace
duplicate primer_bind annotations in the displayed feature
table; only geometry, type and annotation text are passed to feature
layers.
The double backbone establishes one circular sequence. General features use a plasmid preset and explicit stacking Position; labels use the same Position. Primer arcs indicate binding ranges, restriction ticks indicate cleavage/display positions, and the centre label identifies the sequence and length. A cropped or filtered display does not change the full-sequence restriction count.
find_restriction_sites() returns all matches. This
figure explicitly selects EcoRI, BamHI and HindIII so that a compact
restriction cluster remains readable; plasmid_sites retains
the complete search result. The reference filter uses a
saved reference display set only when the complete supplied sequence
matches that reference. Other tasks can select unique,
six_plus or explicit enzyme names. Inspect
display_sites and the site’s methylation/source columns
before drawing. The reference set is a presentation choice, not a claim
about an unknown sample.
primer_names exposes catalogue entries. This example
searches the universal catalogue exactly; reference primer annotations
and exact oligonucleotide searches have distinct provenance. Near-exact
protein feature recognition has the limitations in recognition; it does not validate biological
function or a primer experiment. Text can move, shrink within its
explicit minimum, or hide. Keep the geometry and inspect exported label
reasons at the final size rather than assuming every annotation must
have text.
For your own named DNA sequence, replace the input and inspect search results before adding geoms. For externally curated annotations, pass your feature table instead of running recognition. Use stacking and shapes for geometry, labels for fitting, and output for devices.
geom_primer_label_repel(side = "inside") keeps callouts
inside the backbone, even alongside exterior restriction and feature
labels. This is corrected in the 0.16.6 release; v0.16.5 can move those
requested inside labels outside. The default outside placement and
existing source coordinates are unchanged.
Inside restriction labels also retain their inner geometry and now report the inner side in the exported annotation registry; earlier versions mislabelled those diagnostic rows as exterior resources.
The full inside scene can still place long primer text over other
feature regions. The default max_overlaps = Inf does not
certify zero overlap across feature layers. Prefer the default outside
field when space is tight, or use show_location = FALSE to
shorten the display; inspect the result at its final size.
Start with the centre name and length, then read positions, category colours, arrow directions and outer labels. Check enzyme names in display_sites; selecting a display subset leaves plasmid_sites intact.
Save the plot just created to the working directory; use matching dimensions for preview.
Use the pUC19 example above to learn the layers, then move to the richer pETDuet-1 map. Inputs come from the bundled sequence, feature and reference-primer catalogues. The restriction subset retains unique six-base-or-longer recognition sites from commercial_all; it is not every cut made by every enzyme.
library(ggchord)
library(dplyr)
data(plasmid_example_pETDuet_1)
show_plasmid <- plasmid_example_pETDuet_1 |>
select(accver, length)
show_plasmid_features <- find_common_features(plasmid_example_pETDuet_1)
show_plasmid_primers <- find_primer_bindings(plasmid_example_pETDuet_1, set = "reference")
show_plasmid_all_sites <- find_restriction_sites(plasmid_example_pETDuet_1)
show_plasmid_sites <- filter_restriction_sites(
show_plasmid_all_sites,
set = "unique_6plus", parent_set = "commercial_all"
)show_plasmid_position <- position_feature_stack(
spacing = 0.085, base_position = position_plasmid()
)
p_show_plasmid <- ggchord(show_plasmid, validate = "none") +
geom_seq(seq_style = "double", colour = "#252525") +
geom_feature_plasmid(data = show_plasmid_features, position = show_plasmid_position) +
geom_feature_label_repel(
data = show_plasmid_features, position = show_plasmid_position,
external = TRUE, max_overlaps = 0,
label_fit = "shrink", label_min_size = 2.5
) +
geom_primer(data = show_plasmid_primers, colour = "#A020F0") +
geom_primer_label_repel(
data = show_plasmid_primers, colour = "#A020F0", max_overlaps = 0
) +
geom_restriction_site(data = show_plasmid_sites, colour = "#252525") +
geom_seq_center_label() +
coord_circular(
rotation = 90, fit = "manual",
xlim = c(-3.4, 4.6), ylim = c(-1.6, 2.4)
) +
theme_ggchord_plasmid() +
theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank()
)
p_show_plasmid
Inputs contain 20 features, five reference primers and 58 grouped cut positions. The feature preset retains plum coding regions, mint resistance genes, yellow origins and blue regulatory features; primers are purple, consistently across this site. Dense labels depend on export size; open the full image to read them.
The explicit viewport keeps the dense map at the documented export
size. label_fit = "shrink" allows short-feature labels to
fit down to 2.5 mm; labels that do not fit remain callouts. Preview and
save at the same 16.5 × 8 in size.
Turn file observations into plotting roles. Check minimal fields and coordinates, then use validation reports to decide on cleaning; do not guess missing lengths or strands.
In a fresh R session, run the code below, then follow this page in
order. Colours and layout are optional display choices; inputs select
only the fields needed. For file-based examples, first extract the input
bundle so the working directory contains examples/.
library(ggchord)
library(dplyr)
data(seq_data_example)
data(ribbon_data_example)
data(gene_data_example)
seqs <- seq_data_example |>
select(accver, length)
links <- ribbon_data_example |>
select(qaccver, saccver, qstart, qend, sstart, send)
# The fixture preserves biological strands separately from its old display strands.
genes <- gene_data_example |>
transmute(accver, start, end, strand = source_strand)| Input | Required fields | Meaning |
|---|---|---|
| Sequence | accver, length |
Unique ID; positive full length in bp |
| Interval link | qaccver, saccver, qstart,
qend, sstart, send |
Query and subject anchors |
| Gene | accver, start, end,
strand |
Known +/-; optional anno |
| Feature | accver, start, end,
strand or declared direction |
May retain unknown strand; optional type,
label, feature_id |
Identifiers may be arbitrary strings. Accession versions are
preserved exactly. pident and alignment length
are optional unless a mapping, filter or statistic needs them. A feature
interval does not imply a sequence’s full length.
Genomic coordinates are one-based, inclusive bp.
BED/PAF readers convert their source conventions; do not convert twice.
Gene endpoints are ordered with strand stored separately; link endpoint
order may encode direction. A one-bp link without direction evidence
stays unknown. Unknown strand is never silently changed to
+; use geom_feature() instead of the strict
gene layer.
Amino-acid coordinates require explicit CDS
projection. Native Cartesian layout units, genomic bp, angles in
degrees, physical mm and track value units are different domains. See
the full data contract
(help("ggchord_data_contract", package = "ggchord")).
Run this repository-only example from its root. Original files are not rewritten.
seq_files <- read_fasta_lengths(files = "examples/fasta/*.fna", source_file = TRUE)
link_files <- read_blast(files = "examples/blastn-standard/*.tsv", source_file = TRUE)
feature_files <- read_gff3(files = "examples/gff3/*.gff3", unstranded = "keep", source_file = TRUE)read_genbank() retains record/feature provenance and
segmented locations. read_bed() and read_paf()
retain their conversion metadata. as_ggchord_seq(),
as_ggchord_feature() and as_ggchord_link()
adapt supported S4 objects; provide full lengths and IDs explicitly when
the object lacks them. file and files both
accept path vectors or globs; repeated resolved paths are read once in
first-occurrence order. Choose one clear entry for a workflow. Missing
sequence or homology data must be supplied by the analyst, not invented
by the plotting layer.
After this page’s preparation code:
validation <- validate_ggchord_data(seqs, links, genes)
cleaned <- clean_ggchord_data(seqs, links, genes)
prepared <- prepare_ggchord_data(seqs, links, features = list(genes = genes))Validation reports issues; cleaning makes explicit repairs with a
report. Preparation combines workflow steps without inferring biological
membership. Inspect validation, cleaned$report
and prepared$report before plotting.
source_row in geometry refers to the resolved layer input,
while .source_line means a physical file line; GenBank has
separate record/feature provenance. Aggregates may have
source_rows and .length_estimated flags.
Preserve these when filtering or merging links.
# Deliberately corrupt copies to demonstrate diagnostics; original rows remain intact.
problem_links <- ribbon_data_example |>
slice_head(n = 2) |>
select(qaccver, saccver, qstart, qend, sstart, send, pident) |>
mutate(
qaccver = replace(qaccver, 2, "absent"),
pident = replace(pident, 1, 103)
)
problem_report <- validate_ggchord_data(seqs, problem_links)
repaired <- clean_ggchord_data(seqs, problem_links, unknown_id = "drop", invalid_pident = "clip")| Metric | Observed output |
|---|---|
| Input rows | 2 |
| Rows retained after explicit cleaning | 1 |
The demonstration intentionally includes an unknown ID and identity
outside 0–100 in copied source rows. These explicit repairs are not a
recommendation to clip scientific observations silently. Inspect
problem_report and repaired$report, retain the
original table, and choose policies appropriate to actual evidence. A
legitimate reverse alignment can keep endpoint order instead of being
sorted. prepare_ggchord_data() therefore retains its
workflow-specific default policy; standalone cleaning and preparation
should not be assumed identical.
# Each source record represents a separate complete phage genome.
contigs <- seqs |>
mutate(genome = accver)
genomes <- tibble(genome = seqs$accver)
hierarchy <- validate_ggchord_hierarchy(contigs, genomes, strict = TRUE)hierarchy$contigs summarizes declared membership without
concatenating contigs. No membership is inferred from an accession
prefix. Provide globally unique IDs when separate samples reuse local
contig names. Use file formats and adapters for
format/optional-object details, and the real-record
tutorial to carry reports into a complete figure.
| Metric | Observed output |
|---|---|
| Sequence columns passed to the plot | 2 |
| Link columns passed to the plot | 6 |
| Gene columns passed to the plot | 4 |
The second positional constructor argument is
ribbon_data, not gene_data. Use explicit
argument names as in the shared examples. Optional identity, length and
coverage fields are needed only by operations that consume them. See standard BLAST/protein input and parameter formats.
Input IDs must match complete sequence records. Keep original records separate from repaired copies. Decreasing endpoints can be legitimate reverse hits; warnings do not all mean rows must be removed.
Convert tables, BED, PAF or GenBank into ggchord inputs while retaining record identity and converting coordinate conventions only once.
In a fresh R session, run the code below, then follow this page in
order. Colours and layout are optional display choices; inputs select
only the fields needed. For file-based examples, first extract the input
bundle so the working directory contains examples/.
library(ggchord)
library(dplyr)
data(seq_data_example)
data(ribbon_data_example)
data(gene_data_example)
seqs <- seq_data_example |>
select(accver, length)
links <- ribbon_data_example |>
select(qaccver, saccver, qstart, qend, sstart, send)
# The fixture preserves biological strands separately from its old display strands.
genes <- gene_data_example |>
transmute(accver, start, end, strand = source_strand)
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}
library(dplyr)
data(plasmid_example_pUC19)
plasmid_seq <- plasmid_example_pUC19 |>
select(accver, length)
feature_source <- find_common_features(plasmid_example_pUC19)
features <- feature_source |>
select(accver, start, end, strand, type, anno)The preparation above loads the input tables. These examples rename columns of bundled source-backed sequences, links and genes; they do not invent alignments.
renamed_sequences <- seqs |>
rename(chrom = accver, size = length)
sequence_input <- as_ggchord_seq(renamed_sequences, mapping = c(accver = "chrom", length = "size"))
renamed_features <- genes |>
rename(chrom = accver, left = start, right = end)
feature_input <- as_ggchord_feature(renamed_features,
mapping = c(accver = "chrom", start = "left", end = "right")
)
link_input <- as_ggchord_link(links)Adapters return ordinary frames with ggchord_conversion
provenance. IDs remain unchanged. Supply reliable full lengths rather
than using maximum feature ends. See alignment
input for minimal fields and coordinate units.
The BED example writes known pUC19 intervals as minimal BED3 and reads them back. PAF comes directly from real minimap2 output in the public tutorial, with original optional tags/values. The display retains mapq ≥30 and receives only six endpoint roles.
# BED3 carries intervals only; the source annotations remain in features.
bed_path <- tempfile(fileext = ".bed")
bed_rows <- tibble(accver = features$accver, start0 = features$start - 1L, end = features$end)
write.table(bed_rows, bed_path, sep = "\t", quote = FALSE, row.names = FALSE, col.names = FALSE)
bed_features <- read_bed(bed_path)
unlink(bed_path)
# Unchanged PAF rows from the public EMALE tutorial, not a simulated alignment.
paf_source <- read_paf("examples/format-inputs/emales-pair.paf")
paf_selected <- paf_source |>
filter(!is.na(mapq), mapq >= 30)
paf_links <- paf_selected |>
select(qaccver, saccver, qstart, qend, sstart, send)
paf_seqs <- bind_rows(
transmute(paf_source, accver = qaccver, length = qlen),
transmute(paf_source, accver = saccver, length = slen)
) |>
distinct()
p_paf <- ggchord(seq_data = paf_seqs, ribbon_data = paf_links) +
geom_link_ribbon(fill = "#6BAFA6", alpha = .5) +
geom_seq(colour = "#233E4A") +
geom_seq_label(size = 4) +
coord_collinear() +
doc_axis() +
doc_theme
p_paf
The reader converts BED zero-based half-open intervals to one-based inclusive coordinates; do not add one again. BED3 has no strand, so it returns unknown strand; use a strand-bearing format when needed. Negative PAF relative direction becomes decreasing subject endpoints. These are valid reverse hits, not values to sort indiscriminately. Quality/identity use producer fields, not new alignments.
# The original NCBI L09137.2 record identifies pUC19c; keep that identity.
genbank_result <- NULL
if (nzchar(Sys.which("python3")) && identical(suppressWarnings(system2("python3",
c("-c", shQuote("from Bio import SeqIO")),
stdout = FALSE, stderr = FALSE
)), 0L)) {
genbank_result <- read_genbank("examples/format-inputs/pUC19c-L09137.2.gb")
}This is the original NCBI L09137.2 record, named pUC19c; do not
rename it to the bundled pUC19. Results include seq_data,
features$annotation and report, retaining
segmented locations and record/feature provenance. The selected Python
needs Biopython already installed; no automatic installation occurs.
unsupported = "report" records unsupported locations rather
than guessing bounds.
range_inputs <- NULL
if (requireNamespace("GenomicRanges", quietly = TRUE)) {
unknown <- features |>
filter(strand == ".") |>
slice_head(n = 1)
ranges <- GenomicRanges::GRanges(
seqnames = unknown$accver,
ranges = IRanges::IRanges(unknown$start, unknown$end), strand = "*",
seqlengths = setNames(plasmid_seq$length, plasmid_seq$accver)
)
range_inputs <- list(seq_data = as_ggchord_seq(ranges), features = as_ggchord_feature(ranges))
}interaction_inputs <- NULL
if (requireNamespace("GenomicRanges", quietly = TRUE) &&
requireNamespace("InteractionSet", quietly = TRUE)) {
source_links <- ribbon_data_example |>
slice_head(n = 2)
lengths <- setNames(seq_data_example$length, seq_data_example$accver)
query <- GenomicRanges::GRanges(
seqnames = source_links$qaccver,
ranges = IRanges::IRanges(
pmin(source_links$qstart, source_links$qend),
pmax(source_links$qstart, source_links$qend)
),
strand = ifelse(source_links$qstart <= source_links$qend, "+", "-"),
seqlengths = lengths
)
subject <- GenomicRanges::GRanges(
seqnames = source_links$saccver,
ranges = IRanges::IRanges(
pmin(source_links$sstart, source_links$send),
pmax(source_links$sstart, source_links$send)
),
strand = ifelse(source_links$sstart <= source_links$send, "+", "-"),
seqlengths = lengths
)
interactions <- InteractionSet::GInteractions(query, subject)
interaction_inputs <- list(
seq_data = as_ggchord_seq(interactions),
ribbon_data = as_ggchord_link(interactions)
)
}| Metric | Observed output |
|---|---|
| GenBank optional reader | SKIP: Biopython unavailable |
| Optional interval adapter | PASS |
| Optional GInteractions adapter | PASS |
An originally unknown-strand pUC19 annotation remains unknown as
*. Known full lengths populate Seqinfo.
GInteractions can become links but still needs explicit
sequence/length context. Missing dependencies are marked skipped, not
validated by source presence. See the data contract
(help("ggchord_data_contract", package = "ggchord")).
SAM/BAM CIGAR is not parsed directly: use an alignment-aware tool before
table adaptation; renaming columns alone cannot resolve splicing.
Inspect returned fields and conversion reports before selecting plotting fields. Preserve endpoint direction for reverse PAF hits; do not rename a GenBank record to pretend it is a different bundled plasmid.
Use standard BLAST tabular output without the historical custom 17-column layout. Distinguish file fields, minimal plotting roles and nucleotide versus protein units.
In a fresh R session, run the code below, then follow this page in
order. Colours and layout are optional display choices; inputs select
only the fields needed. For file-based examples, first extract the input
bundle so the working directory contains examples/.
The historical custom list of 17 fields is unnecessary. Use standard tabular output:
blastn -query query.fna -subject subject.fna -outfmt 6 -out result.tsv
blastp -query proteins.faa -subject reference.faa -outfmt 7 -out result.tsvFormat 6 is a table; 7 adds comments.
Without a custom field list both use 12 default fields. Omitting
-outfmt entirely produces pairwise text, which this tabular
reader does not parse. Fields are query ID, subject ID, pident, length,
mismatch, gapopen, qstart, qend, sstart, send, evalue, bitscore. Their
identifier roles become qaccver/saccver;
values are preserved and need not be accession versions. See the NCBI
format documentation.
Twelve file columns do not require twelve plotting columns. After the preparation code above, try the source-backed records:
blast_source <- read_blast("examples/blastn-standard/MT108731.1__MT118296.1.tsv")
blast_links <- blast_source |>
select(qaccver, saccver, qstart, qend, sstart, send)
# A six-column table is sufficient for drawing; extra fields stay in blast_source.
custom_path <- tempfile(fileext = ".tsv")
write.table(blast_links, custom_path, sep = "\t", quote = FALSE, row.names = FALSE, col.names = FALSE)
custom_source <- read_blast(custom_path, format = "custom", col_names = names(blast_links))
custom_links <- custom_source |>
select(all_of(names(blast_links)))
unlink(custom_path)blast_source retains statistics and provenance;
blast_links has six endpoint roles. Identity requires
pident only when mapped/filtered; alignment-length filters
or weights require length.
qlen/slen support the corresponding coverage
calculations. They are not general plotting requirements. A custom
headerless layout requires explicit col_names; arbitrary
six-column meanings are not guessed. Use
as_ggchord_link(x, mapping = ...) for an already-read
non-BLAST table; see format
conversion.
Files in examples/blastn-standard/ retain historical HSP
rows and their first 12 values. They are column conversions, not new
BLAST runs. Old .o7 inputs are unchanged; the conversion
script and hashes record that distinction.
This real BLASTP 2.9.0+ output and query CDS annotation come from the public EMALE tutorial. Retain the original table, then select sufficiently long, low-evalue query HSPs with complete known CDS mappings.
protein_source <- read_blast("examples/format-inputs/emales-mavirus-blastp.tsv", source_file = TRUE)
emale_source <- read_gff3("examples/format-inputs/emales-BVI_023A.gff3", source_file = TRUE)
# The source declares complete Prodigal CDS and translation table 11.
emale_coding <- emale_source |>
filter(grepl("partial=00", attributes)) |>
select(feature_id, accver, start, end, strand, phase)
emale_cds <- build_ggchord_cds(emale_coding, genetic_code = 11)
# Keep strong HSPs whose query protein has a known complete CDS.
# Gaps are retained: projecting one query span does not assert an ungapped pair.
protein_kept <- protein_source |>
filter(qaccver %in% emale_coding$feature_id, evalue <= 1e-10, length >= 50) |>
arrange(desc(bitscore))
selected_hsp <- protein_kept |>
slice_head(n = 1)
query_interval <- tibble(
feature_id = "query_HSP", parent_id = selected_hsp$qaccver,
start = selected_hsp$qstart, end = selected_hsp$qend
)
projected_query <- project_ggchord_features(query_interval, emale_cds)
# Subject projection additionally requires its own CDS map; it is not guessed from an accession.| Metric | Observed output |
|---|---|
| Original BLASTp HSPs | 2948 |
| Complete query CDS records | 24 |
| Query HSPs retained by the explicit filter | 3 |
| Selected query start (aa) | 50 |
| Selected query end (aa) | 289 |
| Projected query-span rows | 1 |
BLASTp endpoints are amino-acid coordinates. This example projects the query HSP span into the known CDS’s genomic coordinates, retaining strand and phase. A gapped HSP still has a projectable query span; that envelope is not a base-by-base nucleotide correspondence. Weak hits, gap counts and statistics remain in the source.
Projecting both ends requires the subject’s own CDS map. It cannot be
inferred from an accession, maximum endpoint or annotation name. The
ungapped = TRUE assertion for
project_ggchord_links() needs evidence. The projection tutorial uses two real plasmid CDS
with identical oriented DNA to demonstrate a justified ungapped
two-ended case.
Native protein backbones with aa axes are outside this version. For
blastx and tblastn, check each endpoint’s
units independently. Parsing does not establish that a table can be
drawn directly in genomic bp coordinates.
Original EMALE records come from the public
gggenomes data/tutorial. Only data records are reused; all figures
are drawn with ggchord, without copying upstream figures or plotting
code. The input bundle’s sources.json records URLs, archive
members, line ranges, hashes and selection. Parsing neither reruns
alignments nor establishes biological function.
A basic link table needs six fields. BLASTp reading retains aa coordinates; use a supported CDS mapping before drawing on this version’s bp axes.
Project known protein intervals to genomic positions through real CDS, retaining phase, strand and segmentation explicitly.
In a fresh R session, run the code below, then follow this page in order. Colours and layout are optional display choices; inputs select only the fields needed.
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}
library(dplyr)
data(plasmid_example_pUC19)
data(plasmid_example_pETDuet_1)
homolog_source <- bind_rows(plasmid_example_pUC19, plasmid_example_pETDuet_1)
homolog_seq <- homolog_source |>
select(accver, length)
cluster_features <- homolog_source |>
find_common_features() |>
filter(type == "CDS", anno == "AmpR") |>
select(accver, start, end, strand, anno) |>
arrange(match(accver, homolog_source$accver)) |>
mutate(feature_id = paste(accver, start, end, sep = ":"))
# Membership is computed from exact strand-corrected CDS DNA equality, not gene names.
reverse_complement <- function(sequence) {
bases <- strsplit(sequence, "")[[1]]
chartr("ACGT", "TGCA", paste(rev(bases), collapse = ""))
}
coding_sequences <- cluster_features |>
left_join(select(homolog_source, accver, sequence), by = "accver") |>
rowwise() |>
mutate(
coding_dna = substr(sequence, start, end),
coding_dna = if (strand == "-") reverse_complement(coding_dna) else coding_dna
) |>
ungroup()
cds_dna <- pull(coding_sequences, coding_dna)
membership <- tibble(
feature_id = cluster_features$feature_id,
cluster_id = paste0("exact_CDS_", match(cds_dna, unique(cds_dna)))
)
homologs <- link_ggchord_clusters(cluster_features, membership,
pairs = tibble(qaccver = homolog_seq$accver[1], saccver = homolog_seq$accver[2])
)
loci <- find_ggchord_loci(homolog_seq, cluster_features,
targets = cluster_features$feature_id[cluster_features$anno == "AmpR"][1], upstream = 100, downstream = 100
)
# Compare equivalent local windows instead of shrinking one gene on full plasmids.
homolog_windows <- cluster_features |>
select(accver, start, end)
local_homologs <- focus_ggchord_data(homolog_seq, homologs$data, cluster_features,
loci = homolog_windows, expand = 200
)
p_homologs <- ggchord(
seq_data = local_homologs$seq_data,
ribbon_data = local_homologs$ribbon_data, gene_data = local_homologs$gene_data
) +
geom_link_ribbon(fill = "#269B82", alpha = .65) +
geom_seq(colour = "#233E4A", show.legend = FALSE) +
geom_feature(
data = local_homologs$gene_data, feature_fill = "#F1D334",
position = position_strand(.10), width = .07
) +
geom_feature_label_repel(data = local_homologs$gene_data, size = 4, label_fit = "shrink") +
geom_seq_label(size = 4) +
geom_seq_break() +
coord_collinear() +
doc_axis("source") +
doc_theme
p_homologsRun the identical-CDS comparison first. It uses real bundled plasmid annotations and DNA. Complete CDS use explicit phase 0 and translation table 11.
# Run the exact-CDS comparison first; these are complete source-backed CDS.
coding <- cluster_features |>
mutate(phase = 0)
cds <- build_ggchord_cds(coding, genetic_code = 11)
# This selects residues for a display example; it does not claim a detected domain.
domains <- tibble(feature_id = "selected_residues", parent_id = coding$feature_id[1], start = 2, end = 20)
genomic <- project_ggchord_features(domains, cds)
genomicThis example selects residues 2–20 for display; it does not claim a detected protein domain. Projection returns genomic intervals with strand/segment context. Actual domains require exact parent CDS IDs and amino-acid ranges; annotation names or maximum feature endpoints cannot supply missing evidence.
build_ggchord_cds() constructs a segment map;
project_ggchord_features() then splits amino-acid intervals
through it. Crossing a boundary can return several rows; multiplying
coordinates by three is insufficient. Inspect policies/reports for
partial CDS, phase and truncation. DNA matching, interval projection and
actual translation/alignment are distinct operations.
# Exact CDS DNA equality supplies evidence for these selected ungapped residues.
matching <- homologs$data |>
slice_head(n = 1)
paired_coding <- cluster_features |>
mutate(phase = 0)
paired_cds <- build_ggchord_cds(paired_coding)
protein_link <- tibble(
qfeature_id = matching$qfeature_id, sfeature_id = matching$sfeature_id,
qstart = 1, qend = 20, sstart = 1, send = 20
)
projected_links <- project_ggchord_links(protein_link, paired_cds, ungapped = TRUE)
projected_linksThis ungapped case relies on verified identical coding DNA. Real BLASTp HSPs may contain gaps: query spans can still be projected, but paired links need the subject CDS and justified ungapped correspondence. See real BLASTp import/projection. This version does not reinterpret aa as bp or add native protein-coordinate plotting.
Check parent CDS IDs, projection reports and returned segments. The selected residue range is a display example, not a detected domain.
Draw real annotations as independent layers, choose suitable shapes and share stacking positions between overlapping intervals and text.
In a fresh R session, run the code below, then follow this page in order. Colours and layout are optional display choices; inputs select only the fields needed.
library(ggchord)
library(dplyr)
data(seq_data_example)
data(ribbon_data_example)
data(gene_data_example)
seqs <- seq_data_example |>
select(accver, length)
links <- ribbon_data_example |>
select(qaccver, saccver, qstart, qend, sstart, send)
# The fixture preserves biological strands separately from its old display strands.
genes <- gene_data_example |>
transmute(accver, start, end, strand = source_strand)
# Optional appearance choices; none of these columns or scales is required input.
library(ggchord)
seq_palette <- setNames(
c("#E64B35", "#233E4A", "#269B82", "#D59B17"),
c("MT108731.1", "MT118296.1", "OQ646790.1", "OR222515.1")
)
gene_palette <- c("+" = "#CE1536", "-" = "#346B9D", "." = "#8291A6")
library(ggchord)
feature_palette <- c(
CDS = "#993366", promoter = "#31849B", rep_origin = "#F1E51D",
primer_bind = "#A020F0", misc_feature = "#A6ACB3", protein_bind = "#31849B"
)
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}
library(dplyr)
data(plasmid_example_pUC19)
plasmid_seq <- plasmid_example_pUC19 |>
select(accver, length)
feature_source <- find_common_features(plasmid_example_pUC19)
features <- feature_source |>
select(accver, start, end, strand, type, anno)The preparation above loads the actual pUC19 annotations; the circular example introduces their roles. This
table retains actual overlapping pUC19 annotations and unknown strands.
Geometry and fill map separately from the same category; an explicit
fixed shape would override shape mapping.
stack_features <- features |>
select(accver, start, end, strand, type, anno)
stack_position <- position_feature_stack(spacing = .12)
feature_shapes <- setNames(ifelse(names(feature_palette) == "CDS", "arrow", "block"), names(feature_palette))
p_stack <- ggchord(seq_data = plasmid_seq) +
geom_seq(show.legend = FALSE) +
geom_feature(aes(feature_fill = type, feature_shape = type),
data = stack_features,
width = .08, position = stack_position
) +
geom_feature_label_repel(data = stack_features, position = stack_position, size = 3.8, label_fit = "shrink", label_min_size = 2.8) +
scale_feature_shape_manual(values = feature_shapes) +
scale_feature_fill_manual(values = feature_palette) +
coord_collinear() +
doc_axis() +
doc_theme
p_stack
position_feature_stack() resolves overlapping annotation
lanes. The label layer uses the same Position, so text anchors follow
those lanes. Prefer unique feature_id values for
subset/reordered label inputs. The strict gene shortcut requires known
+/-; general features retain unknown/bidirectional
semantics.
| Geometry | Typical use | Important distinction |
|---|---|---|
arrow, compact_arrow |
Directional coding/feature interval | Known direction is evidence, not a display guess |
block, capped_line |
Region or unfilled interval | Shape choice does not infer biological function |
promoter_arrow, primer_arrow,
primer_arc |
Declared promoter/primer-style annotation | Computational matching and drawing are separate |
marker, chevron,
lollipop |
Custom interval/point-like emphasis | Keep real interval metadata; display shape is a mapping |
geom_gene() and geom_feature_plasmid() are
useful presets, not aliases with identical defaults. Use
position_strand() for signed offsets and
position_plasmid() as a circular placement preset; the
latter is not a fourth coordinate mode.
regions <- tibble(
accver = links$qaccver[1], start = min(links$qstart[1], links$qend[1]),
end = max(links$qstart[1], links$qend[1]), category = "alignment interval"
)
p_decorated <- ggchord(seq_data = seqs, ribbon_data = links) +
geom_seq_region(aes(region_fill = category), data = regions, colour = NA) +
geom_link_ribbon(fill = "#6BAFA6", alpha = .4) +
geom_seq() +
geom_seq_label(size = 4) +
scale_region_fill_manual(values = c("alignment interval" = "#F4D35E")) +
scale_seq_colour_manual(values = seq_palette) +
geom_seq_scale(length = 10000) +
coord_collinear() +
doc_axis() +
doc_theme
p_decorated
A region overlay uses its own table and region_fill
scale. geom_seq_label() names sequences,
geom_seq_center_label() supplies name/length in a single
circle, and geom_seq_break() marks cropped/segmented ends.
geom_seq_scale() is an independent bp reference bar for
collinear plots; it differs from
scale_seq_position_continuous(), which controls genomic
axes on sequences. An ordinary ggplot2 annotation should use position mapping instead of treating bp as
Cartesian x/y.
Check intervals and strands before shapes and labels. Unknown strands remain in general features; stacking changes display placement, not genomic coordinates.
Arrange text while retaining annotation geometry: choose export dimensions and a label subset before choosing automatic or fixed placement.
In a fresh R session, run the code below, then follow this page in order. Colours and layout are optional display choices; inputs select only the fields needed.
library(ggchord)
library(dplyr)
data(seq_data_example)
data(ribbon_data_example)
data(gene_data_example)
seqs <- seq_data_example |>
select(accver, length)
links <- ribbon_data_example |>
select(qaccver, saccver, qstart, qend, sstart, send)
# The fixture preserves biological strands separately from its old display strands.
genes <- gene_data_example |>
transmute(accver, start, end, strand = source_strand)
# Optional appearance choices; none of these columns or scales is required input.
library(ggchord)
seq_palette <- setNames(
c("#E64B35", "#233E4A", "#269B82", "#D59B17"),
c("MT108731.1", "MT118296.1", "OQ646790.1", "OR222515.1")
)
gene_palette <- c("+" = "#CE1536", "-" = "#346B9D", "." = "#8291A6")
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}Gene labels offer "auto", "radial" and
"arc" layouts. General feature labels additionally support
the internal-first "feature" default and external
"callout" layout. Choose the documented values for the
layer you use; they are not interchangeable presets.
After this page’s preparation code:
label_data <- gene_data_example |>
filter(anno != "hypothetical protein") |>
transmute(accver, start, end, strand = source_strand, anno)
p_labels <- ggchord(seq_data = seqs, gene_data = genes) +
geom_seq() +
geom_gene() +
geom_gene_label_repel(data = label_data, label_layout = "auto", size = 4, label_wrap = 22) +
geom_seq_label(size = 4) +
coord_collinear() +
doc_axis() +
scale_seq_colour_manual(values = seq_palette) +
scale_gene_fill_manual(values = gene_palette) +
doc_theme
p_labels
Use geom_gene_label() or
geom_feature_label() for manual
label_rotation, radial/circumferential offsets and
orientation controls. Automatic layers reject these manual controls:
choose label_layout or remove the manual argument. Use
size, colour and family for
supported fixed text styles.
label_fit, label_wrap,
label_max_lines and label_min_size control
fitting where supported. Shrinking is explicit and bounded. Unresolved
labels may move or hide; the feature geometry remains. Inspect
label_fit_diagnostics and label_diagnostics at
the final export size.
In collinear mode, an omitted feature-label Position inherits an
unambiguous matching feature layer. Unique feature_id
permits reordering or subsets, but accession, interval and strand must
agree. Multiple matching layers need an explicit Position or unambiguous
IDs. Preview after selecting the final device size; resizing the Plots
pane changes the available space.
# Compare layouts with at most the first and last informative label per sequence.
comparison_labels <- label_data |>
group_by(accver) |>
filter(row_number() %in% c(1L, n())) |>
ungroup()
label_plots <- lapply(c("auto", "radial", "arc"), function(layout) {
ggchord(seq_data = seqs, gene_data = genes) +
geom_seq() +
geom_gene() +
geom_gene_label_repel(data = comparison_labels, label_layout = layout, size = 4, label_wrap = 22) +
geom_seq_label(size = 4) +
scale_seq_colour_manual(values = seq_palette, name = "Sequence",
guide = guide_ggchord_legend(nrow = 2, theme = doc_legend_theme)) +
scale_gene_fill_manual(values = gene_palette, breaks = c("+", "-"), name = "Strand", guide = strand_guide) +
coord_chord(xlim = c(-1.8, 1.8), ylim = c(-1.6, 1.6)) +
theme_ggchord_publication(axis = element_blank()) +
doc_legend_theme +
theme(
legend.position = "bottom",
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank()
) +
labs(title = layout)
})
names(label_plots) <- c("auto", "radial", "arc")
p_manual_labels <- ggchord(seq_data = seqs, gene_data = genes) +
geom_seq() +
geom_gene() +
geom_gene_label(data = label_data, label_rotation = 30) +
coord_collinear()
label_plots$auto
Keep at most the first and last informative labels per sequence,
retaining all gene arrows, to compare text orientation. Print
label_plots$auto, $radial or $arc
to inspect a candidate. The inputs and requested font size are
identical; only the layout choice changes. Auto uses shared columns
where applicable, radial follows local contours and arc follows the
sequence tangent. p_manual_labels shows the separate
fixed-label route; its explicit rotation does not belong in an automatic
repel layer. General feature labels have additional
internal-first/callout choices; see the complete
circular workflow for dense mixed annotation and stacking for inherited or explicit
Position anchors.
The three layout comparisons share the same input. Assess text direction, leaders and hiding reasons together; missing text does not mean a feature was lost.
Compute GC fractions from real DNA, then add bars, a moving mean and an annotation band. Each track has an explicit unit, window and baseline.
In a fresh R session, run the code below, then follow this page in order. Colours and layout are optional display choices; inputs select only the fields needed.
library(ggchord)
feature_palette <- c(
CDS = "#993366", promoter = "#31849B", rep_origin = "#F1E51D",
primer_bind = "#A020F0", misc_feature = "#A6ACB3", protein_bind = "#31849B"
)
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}
library(dplyr)
data(plasmid_example_pUC19)
plasmid_seq <- plasmid_example_pUC19 |>
select(accver, length)
feature_source <- find_common_features(plasmid_example_pUC19)
features <- feature_source |>
select(accver, start, end, strand, type, anno)After this page’s preparation code, compute GC fraction in 100 bp windows of bundled pUC19. The final shorter window uses its actual base count.
library(dplyr)
data(plasmid_example_pUC19)
track_seq <- plasmid_example_pUC19 |>
select(accver, length)
starts <- seq.int(1L, track_seq$length, by = 100L)
gc_fraction <- function(sequence) {
bases <- strsplit(sequence, "")[[1]]
mean(bases %in% c("G", "C"))
}
bins <- tibble(
accver = track_seq$accver,
start = starts,
end = pmin(starts + 99L, track_seq$length)
) |>
rowwise() |>
mutate(gc = gc_fraction(substr(plasmid_example_pUC19$sequence, start, end))) |>
ungroup()
head(bins)Each row of bins is an actual sequence window. Check its
endpoints and gc, then map values to track height and
colour.
p_tracks <- ggchord(seq_data = track_seq) +
geom_seq() +
geom_seq_center_label() +
geom_track_bar(aes(track_value = gc, track_fill = gc),
data = bins,
position = position_track("gc", width = unit(10, "mm"))
) +
scale_track_value_continuous("gc", limits = c(0, 1), unit = "GC fraction") +
scale_track_fill_gradientn("gc", colours = c("#E4F3EA", "#65B79B", "#27775C")) +
coord_circular() +
doc_theme
p_tracks
The domain is 0–1, in fractions. Replace it with the actual
unit/range of other measurements. position_track() controls
placement/physical width; scale_track_value_continuous()
controls numerical mapping.
Same-ID layers share a numerical domain, including bar baselines, and must agree on width/placement. Colour scales are scoped independently by track ID. Tiles show interval categories, lines connect observations, bars encode interval values relative to a baseline. Sharing a domain does not require sharing a spatial band.
The export’s track_registry and
track_diagnostics record domains, physical sizes, native
offsets and clipping. Omitted/clipped rows do not mean zero values.
Focus tracks with focus_ggchord_track() using the same
windows.
Run the circular example to obtain real
features first.
line_bins <- bins |>
transmute(
accver,
pos = floor((start + end) / 2),
gc_mean = as.numeric(stats::filter(gc, rep(1 / 3, 3), sides = 2))
) |>
filter(is.finite(gc_mean))
categories <- features |>
select(accver, start, end, type)
p_multitrack <- ggchord(seq_data = track_seq) +
geom_seq() +
geom_seq_center_label() +
geom_track_bar(aes(track_value = gc, track_fill = gc),
data = bins,
position = position_track("gc", width = unit(10, "mm"))
) +
geom_track_line(aes(track_value = gc_mean),
data = line_bins,
position = position_track("gc", width = unit(10, "mm")), colour = "#233E4A"
) +
scale_track_value_continuous("gc", limits = c(0, 1), unit = "GC fraction") +
scale_track_fill_gradientn("gc", colours = c("#E4F3EA", "#65B79B", "#27775C")) +
geom_track_tile(aes(track_fill = type),
data = categories,
position = position_track("features", order = 2, width = unit(2, "mm"))
) +
scale_track_fill_manual("features", values = feature_palette) +
coord_circular() +
doc_theme +
theme(legend.box = "vertical", legend.box.just = "left")
focused_bins <- focus_ggchord_track(bins, track_seq,
loci = tibble(accver = track_seq$accver, start = 500, end = 1500), type = "interval"
)
p_multitrack
Bars and line use the same actual GC measurements; the line is a three-window moving mean. Incomplete end windows are omitted, not filled with invented values. Line positions use integer bin midpoints. Annotation bands have an independent ID and palette. Bands are explicitly ordered outside; overly wide inner bands at small radii can fold and are rejected.
GC in bins lies between 0 and 1; each row is an actual window and the last may be shorter than 100 bp. Continuous colour expresses values; the category band expresses feature types, not GC magnitude.
Give colours a clear meaning: identify categories or continuous quantities, then choose mappings, scales and guides. Website accents do not dictate data colours.
In a fresh R session, run the code below, then follow this page in order. Colours and layout are optional display choices; inputs select only the fields needed.
library(ggchord)
library(dplyr)
data(seq_data_example)
data(ribbon_data_example)
data(gene_data_example)
seqs <- seq_data_example |>
select(accver, length)
links <- ribbon_data_example |>
select(qaccver, saccver, qstart, qend, sstart, send)
# The fixture preserves biological strands separately from its old display strands.
genes <- gene_data_example |>
transmute(accver, start, end, strand = source_strand)
# Optional appearance choices; none of these columns or scales is required input.
library(ggchord)
seq_palette <- setNames(
c("#E64B35", "#233E4A", "#269B82", "#D59B17"),
c("MT108731.1", "MT118296.1", "OQ646790.1", "OR222515.1")
)
gene_palette <- c("+" = "#CE1536", "-" = "#346B9D", "." = "#8291A6")
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}The preparation above declares the inputs and shared appearance. The example declares categories explicitly: ribbon fill maps query ID; sequence colour maps sequence ID; gene fill maps strand. Manual scale names match those values exactly.
p_styled <- ggchord(seq_data = seqs, ribbon_data = links, gene_data = genes) +
geom_seq() +
geom_link_ribbon(aes(ribbon_fill = qaccver), alpha = .5) +
geom_gene() +
geom_seq_label(size = 4) +
coord_collinear() +
doc_axis() +
scale_seq_colour_manual(values = seq_palette) +
scale_gene_fill_manual(values = gene_palette) +
scale_ribbon_fill_manual(values = seq_palette) +
guides(gene_fill = guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)) +
doc_theme
theme_variants <- list(minimal = theme_ggchord_minimal(), dark = theme_ggchord_dark(), plasmid = theme_ggchord_plasmid())
p_styled
| Role | Mapped data | Useful scale family |
|---|---|---|
seq_colour, seq_ring |
Sequence/category | Sequence colour or ring manual scales |
ribbon_fill, ribbon_alpha,
ribbon_colour, ribbon_linetype |
Alignment metadata or statistics | Manual, gradient/steps/identity and alpha scales |
feature_fill, feature_shape,
gene_fill, region_fill |
Annotation category or strand | Role-specific manual/preset scales |
link_colour, link_alpha,
link_linewidth, link_linetype |
Point-link metadata | Continuous or manual link scales |
track_value, track_fill,
track_colour |
Values/categories in one named track | Named value domain and colour/fill scales |
Put a constant style outside aes(). Map variable styles
inside it and supply an appropriate scale. An explicitly fixed geom
value takes precedence; omitting that argument keeps mapping active.
Your scale takes priority over automatic defaults. Repeated same-kind
layers keep independent data/mapping/parameters. A geometric vector
named by accver does not mean a scale also accepts
per-sequence lists.
guide_ggchord_legend() controls discrete role legends;
guide_ggchord_colourbar() controls continuous colour
guides. The colour/color spelling aliases are retained, with
colour preferred in this documentation. Use
show.legend on a layer, guides() for guide
choices and theme(legend.position = ...) for placement. A
wide guide may need multiple rows or a wider export, not smaller
biological data.
The example stores minimal, dark and plasmid alternatives in
theme_variants. Add the appropriate object to the matching
plot. theme_ggchord_publication() is used above; the
plasmid preset belongs with the circular workflow. These objects style
non-data appearance. They do not choose a coordinate system or replace
data-driven fill scales. Use
theme(text = element_text(...)) for text appearance;
removed base_size constructor-style shortcuts have a migration route.
Links from the same query share a colour; continuous identity needs a continuous scale. Put fixed colours outside aes() and make each guide explain its mapping.
Adjust geometry by sequence or strand, retaining useful flexible parameter formats while making their scope explicit.
Previously described as “flexible parameter formats”, this feature configures geometry per sequence or strand.
In a fresh R session, run the code below, then follow this page in order. Colours and layout are optional display choices; inputs select only the fields needed.
library(ggchord)
library(dplyr)
data(seq_data_example)
data(ribbon_data_example)
data(gene_data_example)
seqs <- seq_data_example |>
select(accver, length)
links <- ribbon_data_example |>
select(qaccver, saccver, qstart, qend, sstart, send)
# The fixture preserves biological strands separately from its old display strands.
genes <- gene_data_example |>
transmute(accver, start, end, strand = source_strand)
# Optional appearance choices; none of these columns or scales is required input.
library(ggchord)
seq_palette <- setNames(
c("#E64B35", "#233E4A", "#269B82", "#D59B17"),
c("MT108731.1", "MT118296.1", "OQ646790.1", "OR222515.1")
)
gene_palette <- c("+" = "#CE1536", "-" = "#346B9D", "." = "#8291A6")
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}Sequence-local geometry often needs one value per sequence or strand;
duplicating whole layers would obscure that intent. Keep scalars as the
default, use vectors named by accver when values differ,
and name strand pairs +/-.
# Sequence IDs remain stable when the data is reordered; positional lists do not.
radii <- setNames(c(1.1, 1, 1, 1), seqs$accver)
widths <- c("+" = .045, "-" = .065)
p_parameters <- ggchord(seq_data = seqs, gene_data = genes) +
geom_seq(seq_radius = radii) +
geom_gene(
width = widths,
position = position_strand(c("+" = .08, "-" = -.1))
) +
geom_seq_label(size = 4) +
scale_seq_colour_manual(values = seq_palette) +
scale_gene_fill_manual(values = gene_palette) +
coord_chord() +
doc_theme
p_parametersRun this page’s preparation code first. Named radii survive sequence reordering; unnamed entries follow current positions. These are layout/geometry values, not a universal extension to colours or themes.
| Interface | Parameters | Matching scope |
|---|---|---|
geom_seq() |
seq_radius, seq_curvature,
seq_gap, seq_orientation |
Sequence; radius/curvature effects depend on coordinates |
geom_seq_label() |
labels, seq_label_radius,
seq_label_rotation, size,
seq_label_hjust, seq_label_vjust |
Sequence; seq_label_orientation is one mode choice |
geom_link_ribbon() / geom_link_line() |
link_gap |
Endpoint sequence; Cartesian control points are not per-sequence scalars |
geom_gene() / geom_feature() |
width |
Sequence and/or +/- strand |
position_strand() /
position_plasmid() |
offset |
Sequence/strand; prefer Positions over compatibility offset inputs |
Fixed geom_gene_label() /
geom_feature_label() |
label_radial_offset, label_circum_offset,
label_circum_limit, label_rotation |
Sequence/strand, not every text setting |
position_track() |
Numeric width, gap |
Native numeric placement by sequence; physical unit()
is a scalar absolute size |
seq_order is itself an ordering. Automatic
label_layout is a mode; manual angles and offsets belong to
fixed label layers rather than automatic-label ....
Axes/themes, aesthetics, scales and guides do not inherit the list
extension. Manual scales do accept vectors named by data
categories, under ordinary ggplot2 scale semantics; see styling.
Equal-length unnamed vectors, sequence-named lists, position-named lists and unnamed lists remain compatible. Gene/feature entries can additionally contain strand pairs. Each parameter still validates its own type, length, range and units. This does not make a list a valid container for any argument.
The v0.16.6 RC audit retains these equivalent forms: no deletion or
deprecation is announced. Positional forms follow the current displayed
sequence order after filtering; names take precedence when they match
accession IDs. In width/fixed-label lists, a per-sequence entry is a
strand pair; list(value) is the scalar recycling form.
Position offsets also accept scalar entries per sequence. Explicit
strand-named Position offsets are signed; an omitted strand or sequence
uses zero. position_plasmid() uses one shared offset and
rejects strand pairs. Use unique keys: accession IDs "1",
"+" or "-" can be ambiguous with index/strand
shorthand, so prefer sequence-named nested lists for strand values.
Native track width/gap must cover all displayed sequences. These rules
do not extend to themes.
Numeric values do not all share one unit: seq_gap is a
proportion, rotations are degrees, and physical track sizes use explicit
unit() values. seq_backbone_gap and
seq_backbone_width are scalar style controls, not members
of the list rule. Automatic collinear orientation belongs to
coord_collinear(orientation = "auto");
geom_seq(seq_orientation = ...) supplies numerical +1/-1
sequence directions.
Named-vector keys must match actual accver values or +/− strands. Recheck matching after reordering; list rules do not extend to themes, scales or aesthetics.
Put common adjustments in the appropriate ggplot2 component: mappings, geometry, positions, coordinates and themes have distinct roles.
In a fresh R session, run the code below, then follow this page in order. Colours and layout are optional display choices; inputs select only the fields needed.
library(ggchord)
library(dplyr)
data(seq_data_example)
data(ribbon_data_example)
data(gene_data_example)
seqs <- seq_data_example |>
select(accver, length)
links <- ribbon_data_example |>
select(qaccver, saccver, qstart, qend, sstart, send)
# The fixture preserves biological strands separately from its old display strands.
genes <- gene_data_example |>
transmute(accver, start, end, strand = source_strand)
# Optional appearance choices; none of these columns or scales is required input.
library(ggchord)
seq_palette <- setNames(
c("#E64B35", "#233E4A", "#269B82", "#D59B17"),
c("MT108731.1", "MT118296.1", "OQ646790.1", "OR222515.1")
)
gene_palette <- c("+" = "#CE1536", "-" = "#346B9D", "." = "#8291A6")
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}| Task | Interface |
|---|---|
| Map a column to an aesthetic | aes() |
| Define mapping and guide | scale_*(), guides() |
| Geometry and fixed style | geom_*() |
| Local placement | position_*() |
| Global coordinate system | coord_*() |
| Non-data appearance | theme_*(), theme() |
| Titles and captions | labs() |
Fixed styles may be supplied directly to geoms. Data-driven variation
needs a mapping and scale. An explicit fixed value overrides its
mapping; omitting the fixed argument preserves the mapping. User scales
override inferred defaults. shape is the fixed feature
geometry parameter; feature_shape remains the mapped role.
Explicit shape = NULL is invalid, not a request to use a
mapping.
After this page’s preparation code:
identity_links <- ribbon_data_example |>
select(qaccver, saccver, qstart, qend, sstart, send, pident)
p_mapped <- ggchord(seq_data = seqs, ribbon_data = identity_links) +
geom_seq() +
geom_link_ribbon(aes(ribbon_fill = pident), alpha = .6) +
scale_ribbon_fill_gradientn(
colours = identity_palette, values = identity_values,
limits = c(0, 100), breaks = identity_breaks, name = "Identity (%)", guide = identity_guide
) +
scale_seq_colour_manual(values = seq_palette) +
coord_chord(rotation = 20) +
labs(title = "Sequence comparison") +
doc_theme
p_mappedUse position_strand(c("+" = .1, "-" = -.2)) for signed
strand offsets. position_feature_stack() handles
overlapping features; position_track() allocates a named
quantitative lane. Geometric sequence parameters accept one value, a
matching unnamed vector, or a vector named by accver; named
vectors are recommended. This syntax does not extend to scales, guides
or themes.
Each layer owns its own data, mapping and
parameters. Repeated feature or link layers do not replace one another.
Use ordinary ggplot2 show.legend and
inherit.aes; role input mappings precede geometry, while
staged visual mappings follow ggplot2 evaluation. See the API contract
(help("ggchord_api_contract", package = "ggchord")).
Use a coordinate for global rotation, named values for per-sequence geometry, and mappings plus scales for data-dependent colour.
Inspect figures at their actual export dimensions, save with standard ggsave(), and understand what layout tables and diagnostics establish.
In a fresh R session, run the code below, then follow this page in order. Colours and layout are optional display choices; inputs select only the fields needed.
library(ggchord)
library(dplyr)
data(seq_data_example)
data(ribbon_data_example)
data(gene_data_example)
seqs <- seq_data_example |>
select(accver, length)
links <- ribbon_data_example |>
select(qaccver, saccver, qstart, qend, sstart, send)
# The fixture preserves biological strands separately from its old display strands.
genes <- gene_data_example |>
transmute(accver, start, end, strand = source_strand)
# Optional appearance choices; none of these columns or scales is required input.
library(ggchord)
seq_palette <- setNames(
c("#E64B35", "#233E4A", "#269B82", "#D59B17"),
c("MT108731.1", "MT118296.1", "OQ646790.1", "OR222515.1")
)
gene_palette <- c("+" = "#CE1536", "-" = "#346B9D", "." = "#8291A6")
library(ggchord)
doc_background <- "#FFFCF5"
identity_palette <- c("#303B71", "#36537D", "#269B82", "#A5D52C", "#ECEB31")
identity_values <- c(0, 0.5, 0.8, 0.9, 1)
identity_breaks <- c(0, 50, 80, 90, 100)
doc_legend_theme <- theme(
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.margin = margin(l = 4, r = 4, unit = "mm")
)
sequence_guide <- guide_ggchord_legend(nrow = 1, theme = doc_legend_theme)
strand_guide <- guide_ggchord_legend(
nrow = 1, override.aes = list(strand = c("+", "-")), theme = doc_legend_theme
)
identity_guide <- guide_ggchord_colourbar(
position = "left", direction = "vertical",
theme = theme(
legend.key.height = unit(75, "mm"), legend.key.width = unit(5, "mm"),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11), legend.margin = margin(r = 7, unit = "mm")
)
)
doc_theme <- theme_ggchord_publication(
axis.text = element_text(size = 11),
axis.text.check.overlap = TRUE
) +
theme(
legend.position = "bottom",
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.key.width = unit(9, "mm"), legend.key.height = unit(5, "mm"),
legend.spacing.x = unit(9, "mm"), legend.spacing.y = unit(7, "mm"),
legend.box.spacing = unit(6, "mm"),
plot.background = element_rect(fill = doc_background, colour = NA),
panel.background = element_blank(),
plot.title = element_text(face = "bold", size = 18),
plot.subtitle = element_text(size = 10, colour = "#5C625A"),
plot.margin = margin(14, 18, 14, 18)
)
doc_axis <- function(coordinates = "relative") {
scale_seq_position_continuous(
coordinates = coordinates, n.breaks = 3, minor_breaks = NULL,
labels = function(x) format(x, scientific = FALSE, big.mark = ",", trim = TRUE)
)
}
anchors <- genes |>
group_by(accver) |>
slice_max(end - start, n = 1, with_ties = FALSE) |>
ungroup() |>
select(accver, start, end)
p_collinear <- ggchord(seq_data = seqs, ribbon_data = links, gene_data = genes) +
geom_link_ribbon(aes(ribbon_fill = qaccver), alpha = .7) +
scale_ribbon_fill_manual(values = seq_palette, guide = "none") +
geom_seq() +
geom_gene(position = position_strand(.1)) +
geom_seq_label(size = 4) +
coord_collinear(anchor = anchors, align = "center", orientation = "auto") +
doc_axis() +
scale_seq_colour_manual(values = seq_palette) +
scale_gene_fill_manual(values = gene_palette) +
doc_theme
p_collinearAfter creating p_collinear in three
modes:
RStudio’s Plots pane uses the current panel size and can rearrange
labels and guides. Preview at the same explicit dimensions before final
ggsave():
view_ggchord(p_collinear, width = 8, height = 5, units = "in")
diagnosis <- diagnose_ggchord(p_collinear, width = 8, height = 5, units = "in")view_ggchord() returns a temporary image path; it does
not keep a final export. The website, README and vignettes share PNG
assets exported through standard ggsave() at sizes listed
in the gallery. They do not capture an
interactively resized panel. Use your intended PDF/SVG device explicitly
for final vector output; font availability and embedding remain
device-dependent.
geometry <- export_ggchord_layout(p_collinear, include = "seq")
rows <- geometry$seq |>
distinct(layer_id, component, source_row)export_ggchord_layout() is the recommended layer-aware
table interface. get_chord_layout() retains the complete
documented layout; neither is deprecated. Select named fields, not fixed
column positions. source_row indexes resolved layer input;
IDs are build-local resources, not permanent biological identifiers. See
the complete layout contract
(help("ggchord_layout_contract", package = "ggchord")).
diagnose_ggchord() separates measured panel size from
geometry convergence. A measured panel does not certify collision-free
text, font embedding or all applications. Inspect warnings, text/guide
measurements and placement reasons.
optimize_ggchord_layout() applies documented opt-in
adjustments; retain a copy and inspect the result. Previews and
diagnostics use independent render copies. Direct mutation of shared
ggproto layer environments retains ordinary ggplot2 reference semantics;
it is not an automatically isolated edit.
full_layout <- get_chord_layout(p_collinear)
layer_tables <- export_ggchord_layout(p_collinear, include = c("seq", "gene"), original_data = TRUE)
layout_choice <- optimize_ggchord_layout(seqs, links, optimize = c("order", "orientation"), weight = "count")Create p_collinear in three
modes first. full_layout retains the complete
documented getter result. layer_tables selects
sequence/gene tables and resolved original data; it is not a raw-file
archive. Layer IDs and source-row roles distinguish independent
same-kind layers. Empty components can lack a fixed column prototype.
Select fields by name.
layout_choice optimizes declared order/orientation using
count weighting because the minimal plotting table deliberately omits
alignment-length statistics. It returns a choice for explicit use in
sequence settings; it does not mutate the plot or establish biological
orientation. Save the choice and compare the result before adopting it.
The export asset manifest (website/assets/plots.csv in the
source repository) records exact documentation sizes.
Use matching dimensions for preview and saving. Inspect text, guides and warnings; a successful measurement does not certify fonts or release quality across devices.
| Capability | Read | Executable source / output |
|---|---|---|
| Three coordinate modes | Choose your learning module | chord, circular, collinear ·
chord, circular, collinear |
| Original-record comparisons | 1.6 Full source-file workflow | realimport, realprepare,
realchord, reallinear · realchord,
reallinear |
| Complete circular annotation | 3.5 Complete plasmid cases | plasmid · plasmid |
| Validation, cleaning and hierarchy | Prepare tables and coordinates | validation, cleaning,
hierarchy |
| Formats and adapters | Import and convert data, Import nucleotide and protein alignments | adapters, formats, genbank,
optionalranges, standardblast,
protein · paf |
| Link preparation and statistics | 1.5 Filter and summarize links | ribbons · density |
| Interval and point links | 1.5 Filter and summarize links, Choose your learning module | pointlinks, collinear · pointlinks |
| Features and Position | Features, stacking and genomic decoration | featurestack · featurestack |
| Automatic and fixed labels | Labels and local placement, 3.5 Complete plasmid cases | labellayouts, labels, plasmid
· labelauto, labelradial, labelarc |
| Axes, scale bars and regions | Features, stacking and genomic decoration, 2.5 Focus, anchors and segments | decorations, focus · decorations |
| Quantitative tracks and domains | Quantitative tracks | tracks, multitrack · multitrack |
| Primers, sites and feature search | 3.3 Features, primers and restriction, 3.5 Complete plasmid cases | recognition, plasmid · plasmid |
| CDS and link projection | Project protein intervals to genomic coordinates, 2.4 Compare CDS neighbourhoods | projection, linkprojection |
| Explicit clusters and loci | 2.4 Compare CDS neighbourhoods | homologs · homologs |
| Focus, segments and source mapping | 2.5 Focus, anchors and segments | focus, segment,
mappingposition · focus |
| Scales, guides and themes | Mappings, scales, guides and themes, Common adjustments and grammar, Set values by sequence and strand | styling, adjustments,
parameters · styling |
| Preview, results and ordering | Export, preview and diagnostics | save, preview, provenance,
layoutresults |
| Migration and FAQ | Migrate to the candidate API, Frequently asked questions | migration |
Choose an example by the question it answers. Thumbnails help you navigate; click for full size. Every figure links to preparation, code and interpretation.
| Overall relationships | One annotated sequence | Local positions | Quantities and text |
|---|---|---|---|
| Chord | Plasmid | CDS neighbourhood | GC tracks |
| Point links | Feature stacking | Focus | Label layouts |
Data, code and interpretation →
p_chord · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_circular · 9 × 8 in · 144 dpi
Data, code and interpretation →
p_collinear · 10 × 6 in · 144 dpi
Data, code and interpretation →
p_tracks · 9 × 8 in · 144 dpi
Data, code and interpretation →
p_focus · 10 × 6 in · 144 dpi
Data, code and interpretation →
p_labels · 10 × 6 in · 144 dpi
Data, code and interpretation →
p_real_chord · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_real_linear · 10 × 6 in · 144 dpi
Data, code and interpretation →
p_plasmid · 10 × 9 in · 144 dpi
Data, code and interpretation →
p_density · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_points · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_stack · 10 × 5 in · 144 dpi
Data, code and interpretation →
p_multitrack · 9 × 8 in · 144 dpi
Data, code and interpretation →
p_homologs · 10 × 5 in · 144 dpi
Data, code and interpretation →
p_decorated · 10 × 6 in · 144 dpi
Data, code and interpretation →
p_labelauto · 12 × 10 in · 144 dpi
Data, code and interpretation →
p_labelradial · 12 × 10 in · 144 dpi
Data, code and interpretation →
p_labelarc · 12 × 10 in · 144 dpi
Data, code and interpretation →
p_styled · 10 × 6 in · 144 dpi
Data, code and interpretation →
p_paf · 10 × 5 in · 144 dpi
Data, code and interpretation →
p_first · 10 × 5 in · 144 dpi
Data, code and interpretation →
p_show_chord · 12 × 10 in · 144 dpi
Data, code and interpretation →
p_show_plasmid · 16.5 × 8 in · 144 dpi
Data, code and interpretation →
p_show_collinear · 12 × 8 in · 144 dpi
Data, code and interpretation →
p_learn_seq · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_learn_links · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_learn_genes · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_learn_labels · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_linear_seq · 10 × 6 in · 144 dpi
Data, code and interpretation →
p_linear_links · 10 × 6 in · 144 dpi
Data, code and interpretation →
p_linear_labels · 10 × 6 in · 144 dpi
Data, code and interpretation →
p_plasmid_backbone · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_plasmid_features · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_plasmid_text · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_catalogpsb · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_catalogpuc · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_catalogpbr · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_catalogblue · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_catalogpet · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_catalogduet · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_catalogdonr · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_catalogpcdna · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_catalogtre · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_catalogtripz · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_catalogcambia · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_catalogearley · 10 × 8 in · 144 dpi
Data, code and interpretation →
p_catalogcas · 10 × 8 in · 144 dpi
All figures use bundled source-backed data or attributed real input
records, defined in the shared documentation
example source. No raw FASTA/GFF3/BLAST record was changed to
improve appearance. The same image bytes are used by both languages,
README and vignette; captions provide localized interpretation. The
source and asset hashes are recorded in the asset manifest
(website/assets/plots.csv in the source repository).
Display filters are stated in the relevant articles. GC is computed from actual DNA; exact-CDS membership is checked from sequence content. Deliberate corruption is confined to diagnostic examples and is never presented as biological data.
From the source repository, with its suggested development dependencies available:
Rscript tools/documentation-plots.R
python3 tools/sync-tutorials.py --check
python3 tools/build-website.py
python3 tools/check-documentation.pyThe PNG device is used only to update these documentation figures.
This is not an output-device matrix or release validation. Source code
and dimensions live in tools/documentation-plots.R;
changing the figure parameters requires regenerating the manifest. See
output for your own export.
The available physical space changed. Use explicit-size preview and saving. Check hidden-label diagnostics; do not infer missing input rows from absent text.
The API cleanup removed execution of old aliases. Use the migration table. Do not mix old and new spellings
for one semantic setting. The colour/color function spelling aliases
remain, with colour preferred in documentation.
The gene shortcut requires known +/-
strand. Keep unknown strand in a general feature layer; do not guess
+. Amino-acid intervals require explicit projection.
By default ribbons connect adjacent displayed sequences. Inspect pair
selection, clipping and validation. Use explicit pairs or
adjacent_only = FALSE deliberately. This default does not
remove original input records.
It visualizes supplied alignments and computational search results. It does not run BLAST, infer clusters from names or validate wet-lab outcomes. Search confidence is not a probability. See recognition limitations.
No. It describes a requested render. Platform/device/font checks, full vignettes and package checks belong to an explicitly authorized release. See the release acceptance record for the actual scope of verification.
Use the issue tracker with a small input, package/ggplot2 versions, code, intended width/height, device and diagnostic output. Avoid attaching private sequence data; a synthetic reproduction is usually enough.
These are current candidate replacements, not a v1.0.0 compatibility promise. The 0.16.0–0.16.4 phases were delivered together in v0.16.5. The current installation tag is v0.16.6. The RC audit retains flexible parameter formats, spelling aliases, reexports and getter/export entry points without a new deprecation. Old calls in this table explain migration and must not be executed as current examples. Exact first-removal tags are not guessed where historical evidence gives only an upper bound.
| Old (do not run) | Current | Removal / guard | Meaning |
|---|---|---|---|
geom_ribbon() |
geom_link_ribbon() |
≤ 0.16.1 | Renamed link layer; not ggplot2::geom_ribbon(). |
ggchord(..., rotation = 20) |
ggchord(...) + coord_chord(rotation = 20) |
≤ 0.16.1 | Global coordinates are composed with +. |
geom_feature(feature_shape = "block", feature_width = .05) |
geom_feature(shape = "block", width = .05) |
0.16.1 | Mapped feature_shape is retained. |
geom_gene(gene_offset = c("+" = .1, "-" = .2)) |
geom_gene(position = position_strand(c("+" = .1, "-" = -.2))) |
0.16.1 | Explicit Position values are signed. |
geom_feature(feature_offset = .1) |
geom_feature(position = position_strand(.1)) |
0.16.1 | Use a Position object. |
geom_gene(gene_width = .05) |
geom_gene(width = .05) |
0.16.1 | Fixed geometry uses the shared width name. |
geom_link_ribbon(ribbon_gap = .01) |
geom_link_ribbon(link_gap = .01) |
0.16.1 | Use link_gap; likewise use link_ctrl_point. |
geom_gene_label_repel(gene_label_layout = "aligned") |
geom_gene_label_repel(label_layout = "auto") |
0.16.1 | Migrate both the name and value. |
geom_gene_label_repel(label_rotation = 90) |
geom_gene_label(label_rotation = 90) |
0.16.4 guard | Previously ignored manual control now errors; alternatively remove it. |
geom_feature_label_repel(label_orientation = "horizontal") |
geom_feature_label(label_orientation = "horizontal") |
0.16.4 guard | Use the fixed feature-label layer for unknown-strand features. |
geom_restriction_site(leader = "trunk") |
geom_restriction_site(leader = "radial") |
0.16.1 | The radial default remains. |
theme_ggchord(base_size = 10) |
theme_ggchord() + theme(text = element_text(size = 10)) |
≤ 0.16.1 | Text appearance belongs to theme elements. |
In a fresh session, run this preparation before the current replacements:
library(ggchord)
library(dplyr)
data(seq_data_example)
data(ribbon_data_example)
data(gene_data_example)
seqs <- seq_data_example |>
select(accver, length)
links <- ribbon_data_example |>
select(qaccver, saccver, qstart, qend, sstart, send)
# The fixture preserves biological strands separately from its old display strands.
genes <- gene_data_example |>
transmute(accver, start, end, strand = source_strand)
library(dplyr)
data(plasmid_example_pUC19)
plasmid_seq <- plasmid_example_pUC19 |>
select(accver, length)
feature_source <- find_common_features(plasmid_example_pUC19)
features <- feature_source |>
select(accver, start, end, strand, type, anno)p_migrated <- ggchord(plasmid_seq) +
geom_seq() +
geom_feature(data = features, shape = "block", width = .05) +
geom_feature_label(data = features, label_rotation = 0) +
coord_circular()
p_offsets <- ggchord(seqs, gene_data = genes) +
geom_seq() +
geom_gene(position = position_strand(c("+" = .1, "-" = -.2)))Old minus-strand offset magnitudes need a negative sign in explicit
Position values. Mapping still uses
aes(feature_shape = type) with a shape scale; the fixed
parameter is shape. Automatic labels use
label_layout; manual geometry uses the corresponding
fixed-label layer. label_layout = "aligned" becomes
"auto". Removed names are errors, not silently retained
execution paths.
The five colour/color spelling aliases, all upstream reexports,
gene/feature/plasmid shortcuts and both geometry getter/export
interfaces remain. Coordinate-specific defaults and already documented
result fields are retained. See the API contract
(help("ggchord_api_contract", package = "ggchord")) and
current reference for NULL/NA/missing semantics; NULL does not
universally mean disabled.
| English | 现代汉语 | API / units |
|---|---|---|
| sequence | 序列 | accver / length |
| chord diagram | 弦图 | coord_chord() |
| circular sequence map | 单序列圆图 | coord_circular() |
| collinearity plot | 共线性图 | coord_collinear() |
| interval link / ribbon | 区间连接 / 连接带 | qaccver / saccver |
| feature | 通用注释特征(feature) | geom_feature() |
| gene | 已知链的基因 | geom_gene() |
| strand | 链方向 | + / - / unknown |
| aesthetic mapping | 美学属性映射 | aes() |
| scale | 标度 | scale_*() |
| guide | 图例 / 标度指南 | guides() |
| position | 局部位置调整 | position_*() |
| track | 定量轨道 | position_track() |
| genomic position | 基因组位置 | one-based inclusive bp / 一基闭区间 |
| layout units | 原生布局单位 | not bp or mm / 不等于 bp 或 mm |
| source row | 已解析输入来源行 | source_row |
| physical file line | 物理文件行 | .source_line |
| focus | 局部聚焦 | focus_ggchord_data() |
| segment | 分段窗口 | segment_ggchord_data() |
| leader | 标签引导线 | label / connection |
| CDS projection | 编码序列坐标投影 | build_ggchord_cds() |
| measured panel | 已测量面板 | not visual certification / 不等于视觉认证 |
The grammar follows ggplot2: mappings, scales, geometry, positions, coordinates and themes retain distinct responsibilities. Compact query/subject field names follow BLAST-style tables; ggchord does not run an alignment tool during drawing. Circular hierarchy and feature readability are compared with Circos, clinker and DNA Features Viewer, as recorded in the repository design documents. These are conceptual comparisons, not copied APIs.
The detailed ggchord artwork was supplied by the user on 2026-11-01
for this website and documentation. Full-size PNG and WebP preserve
every decoded RGBA pixel. Only the small navigation thumbnail is
resized. See the image manifest (website/assets/icon.json
in the source repository) for source hashes and exact sizes. The compact
hexagonal study mark below, site CSS/JS and documentation diagrams are
original project assets under the repository MIT terms. The study mark
is supplementary, not a replacement for the supplied artwork.
System fonts are used; no webfont,
icon library or third-party image is downloaded. Plot colours come from
the package’s existing defaults or explicit teaching code; those literal
colour choices are listed with the source. The site uses the existing
Pandoc and R documentation converters at build time without bundling
their binaries or stylesheets.