# ggchord tutorial

    library(ggchord)
    library(ggplot2)

    data(seq_data_example)
    data(ribbon_data_example)
    data(gene_data_example)

This tutorial walks through the complete `ggchord` workflow: preparing input data, importing files in R, validating and cleaning data, and building plots layer by layer.

## 1. Data preparation

Three types of input data are used. All three are plain data frames.

### [Required] Sequence data (`seq_data`)

Column | Type | Description
--- | --- | ---
`seq_id` | character | Unique sequence identifier
`length` | integer | Sequence length (must be positive)

Example:

seq_id | length
--- | ---
MT108731.1 | 64323
MT118296.1 | 32090
OQ646790.1 | 57367
OR222515.1 | 83080

A common way to build this table from FASTA files:

    seqkit fx2tab -nil examples/fasta/*.fna | sed '1i seq_id\tlength' > examples/seq_track.tsv

### [Optional] Alignment data (`ribbon_data`)

One row per alignment block between two sequences (the column names follow common alignment-tool conventions):

Column | Type | Description
--- | --- | ---
`qaccver` | character | Query sequence ID
`saccver` | character | Subject sequence ID
`length` | integer | Alignment length (bp)
`pident` | numeric | Percent identity (0–100)
`qstart` | integer | Start position on the query sequence
`qend` | integer | End position on the query sequence
`sstart` | integer | Start position on the subject sequence
`send` | integer | End position on the subject sequence

Example rows:

qaccver | saccver | length | pident | qstart | qend | sstart | send
--- | --- | --- | --- | --- | --- | --- | ---
MT108731.1 | MT118296.1 | 24856 | 98.612 | 26298 | 51139 | 7121 | 31959
MT108731.1 | MT118296.1 | 4412 | 97.031 | 21513 | 25922 | 2365 | 6772
MT108731.1 | MT118296.1 | 464 | 94.181 | 20691 | 21146 | 1032 | 1495

For example, the standard BLAST `-outfmt 7` output can be parsed into this table directly:

    seqs=("MT108731.1" "MT118296.1" "OQ646790.1" "OR222515.1")
    ext="fna"
    for ((i=0; i<${#seqs[@]}-1; i++)); do
      for ((j=i+1; j<${#seqs[@]}; j++)); do
        blastn \
          -outfmt '7 qaccver saccver pident length mismatch gapopen qstart qend sstart send evalue bitscore qcovs qlen slen sstrand stitle' \
          -query "examples/fasta/${seqs[$i]}.${ext}" \
          -subject "examples/fasta/${seqs[$j]}.${ext}" \
          -out "examples/blastn/${seqs[$i]}__${seqs[$j]}.o7"
      done
    done

### [Optional] Gene data (`gene_data`)

One row per gene (or feature) on a sequence:

Column | Type | Description
--- | --- | ---
`seq_id` | character | Sequence ID the gene belongs to
`start` | integer | Gene start position
`end` | integer | Gene end position
`strand` | character | Strand direction (`+` or `-`)
`anno` | character | Gene annotation / functional category

Example rows:

seq_id | start | end | strand | anno
--- | --- | --- | --- | ---
MT108731.1 | 60709 | 63087 | + | hypothetical protein
MT118296.1 | 14628 | 16301 | + | virion structural protein
OQ646790.1 | 43765 | 46140 | + | integrase
OQ646790.1 | 13194 | 15551 | + | tail tape measure protein

For example, this table can be converted from GFF3 files:

    library(tidyverse)
    gff3FilesPath <- list.files(path = "examples/gff3", pattern = "\\.gff3$", full.names = TRUE)
    gff3Table <- map_df(gff3FilesPath, ~read_tsv(.x, show_col_types = F, comment = "#",
      col_names = F) %>% set_names(c("seq_id", "source", "type", "start", "end",
      "score", "strand", "phase", "attributes")))
    geneTrackTable <- gff3Table %>%
      filter(type == "CDS") %>%
      mutate(anno = str_extract(attributes, "(?<=product=)[^;]+(?=;)")) %>%
      select(seq_id, start, end, strand, anno)
    write_tsv(geneTrackTable, "examples/gene_track.tsv")

## 2. Importing FASTA / BLAST / GFF3 data in R

External command-line tools (BLAST, seqkit, …) are often used to prepare the data, but the files they produce can also be read directly in R with the built-in import helpers. This keeps the “prepare data outside R” and “import + plot in R” steps clearly separated:

    library(ggchord)

    # FASTA -> seq_data (read and combine all example FASTA files)
    seq_data <- invisible(read_fasta_lengths(files = "examples/fasta/*.fna"))

    # BLAST -outfmt 6/7 tabular output -> ribbon_data (12 or 17 columns auto-detected)
    ribbon_data <- invisible(read_blast(files = "examples/blastn/*.o7"))

    # GFF3 -> gene_data (CDS features by default; anno from product/Name/...)
    gene_data <- invisible(read_gff3(files = "examples/gff3/*.gff3"))

    ggchord(seq_data, ribbon_data, gene_data) +
      geom_seq() + geom_ribbon() + geom_gene()

`read_blast()` preserves useful extra columns (`evalue`, `bitscore`, `qcovs`, `qlen`, `slen`, `sstrand`, `stitle`); `read_gff3()` preserves `type`, `source`, `score`, `phase` and `attributes`; `read_fasta_lengths()` optionally splits NCBI-style headers with `header_delim = "|"`.

## 3. Tutorial: from data to a diagram

This section turns the prepared data into a complete chord diagram. Each step shows a short, runnable example and its corresponding figure.

### 3.1 Validate and clean input data

    data(seq_data_example)
    data(ribbon_data_example)
    data(gene_data_example)

    validate_ggchord_data(seq_data_example, ribbon_data_example, gene_data_example)

    clean_ggchord_data(seq_data_example, ribbon_data_example, gene_data_example,
                       unknown_id = "drop", out_of_range = "clip",
                       reversed_interval = "sort", invalid_pident = "clip")

`ggchord()` also validates automatically with `validate = "warn"`. Use `"error"` to stop on severe problems or `"none"` for large inputs.

### 3.2 Filter and merge ribbons

    kept <- filter_ggchord_ribbons(ribbon_data_example, min_pident = 90,
                                   drop_self_links = TRUE,
                                   sort_by = "pident")
    dedup <- deduplicate_ggchord_ribbons(kept$data, by = "exact",
                                         keep = "best_pident")
    merged <- merge_ggchord_ribbons(dedup$data, max_gap = 0)

The returned `$data` tables can be passed directly to `ggchord()`.

### 3.3 Start with sequence arcs

    ggchord(seq_data_example) +
      geom_seq()

Sequence arcs.

### 3.4 Add alignment ribbons

    ggchord(seq_data_example, ribbon_data_example) +
      geom_seq() + geom_ribbon()

Ribbons coloured by percent identity.

### 3.5 Add genes and labels

    ggchord(seq_data_example, gene_data = gene_data_example) +
      geom_seq() + geom_gene() + geom_gene_label_repel()

Gene arrows with repelled labels.

### 3.6 Add axes and sequence labels

    ggchord(seq_data_example) +
      geom_seq() + geom_axis() + geom_seq_label()

Axes and sequence labels.

### 3.7 Group sequences

    seq_grouped <- transform(seq_data_example,
                             seq_group = c("host", "host", "phage", "phage"))

    ggchord(seq_grouped, ribbon_data_example, gene_data_example) +
      geom_seq(seq_group = "seq_group",
               seq_group_colors = c(host = "#E41A1C", phage = "#377EB8")) +
      geom_ribbon() + geom_gene()

Sequences grouped with an inter-group gap and labels.

### 3.8 Map numeric ribbon columns and direction

    rb_scored <- transform(ribbon_data_example,
                           bitscore = seq_len(nrow(ribbon_data_example)) * 10)

    ggchord(seq_data_example, rb_scored) +
      geom_seq() +
      geom_ribbon(ribbon_color_by = "bitscore",
                  ribbon_alpha_by = "bitscore",
                  ribbon_direction = "linetype")

Continuous ribbon fill, alpha and direction mapping.

### 3.9 Highlight regions and ribbons

    regions <- data.frame(seq_id = "MT108731.1",
                          start = 1000, end = 4000, color = "orange")

    ggchord(seq_data_example, ribbon_data_example) +
      geom_seq() + geom_ribbon() +
      geom_seq_region(regions = regions) +
      geom_ribbon_highlight(ribbon_ids = 1)

Sequence regions and ribbon highlight.

### 3.10 Draw generic features

    features <- data.frame(seq_id = c("MT108731.1", "MT118296.1"),
                           start = c(1000, 500), end = c(4000, 2000),
                           strand = c("+", "-"), type = c("CDS", "tRNA"))

    ggchord(seq_data_example, ribbon_data_example) +
      geom_seq() + geom_ribbon() + geom_feature(features)

Generic features drawn with geom_feature().

### 3.11 Apply themes and scales

    ggchord(seq_data_example, ribbon_data_example, gene_data_example) +
      geom_seq() + geom_ribbon() + geom_gene() + geom_axis() +
      scale_color_manual(values = c("MT108731.1" = "#E41A1C",
                                    "MT118296.1" = "#377EB8",
                                    "OQ646790.1" = "#4DAF4A",
                                    "OR222515.1" = "#984EA3")) +
      theme(panel.background = element_rect(fill = "grey95"),
            legend.position = "bottom", legend.box = "horizontal")

A themed plot with all legends grouped.

### 3.12 Fine-tuned publication-style plot

    ggchord(seq_data_example, ribbon_data_example, gene_data_example,
            title = "ggchord") +
      geom_seq(seq_radius = c(3.3, 2.5, 1.8, 1.25),
               seq_orientation = c(-1, -1, 1, -1),
               seq_colors = c("MT108731.1" = "#E76F51",
                              "MT118296.1" = "#264653",
                              "OQ646790.1" = "#2A9D8F",
                              "OR222515.1" = "#D9A62E")) +
      geom_ribbon(ribbon_alpha = 0.45) +
      geom_gene() +
      geom_gene_label_repel(gene_label_size = 2, seed = 42) +
      geom_seq_label() +
      geom_axis() +
      theme(plot.background = element_rect(fill = "#FBF9F6", colour = NA),
            panel.background = element_rect(fill = "#FBF9F6", colour = NA))

A complete fine-tuned chord diagram.

## 6. Flexible parameter formats

Sequence-level parameters (`seq_radius`, `seq_gap`, `axis_label_size`, …) accept a single value, an unnamed vector, a vector/list named by sequence ID, a list named by sequence order (`"1"`, `"2"`, …), or an unnamed list. Gene-level parameters (`gene_label_rotation`, `gene_offset`, …) additionally accept per-strand (`+`/`-`) values. All of the following are valid:

    # 1. One value for everything
    gene_label_rotation = 20

    # 2. One value per strand, for every sequence
    gene_label_rotation = c("+" = -15, "-" = -45)

    # 3. By sequence ID
    gene_label_rotation = list(
      "MT118296.1" = c("+" = -15, "-" = -45),
      "OR222515.1" = c("+" = 30, "-" = -30),
      "MT108731.1" = c("+" = 15, "-" = -15),
      "OQ646790.1" = c("+" = 0,  "-" = 0)
    )

    # 4. By sequence order ("1" = first sequence)
    gene_label_rotation = list(
      "1" = c("+" = -15, "-" = -45),
      "2" = c("+" = 30, "-" = -30),
      "3" = c("+" = 15, "-" = -15),
      "4" = c("+" = 0,  "-" = 0)
    )

    # 5. Unnamed list: follows the sequence order (same as #4)
    gene_label_rotation = list(
      c("+" = -15, "-" = -45),
      c("+" = 30, "-" = -30),
      c("+" = 15, "-" = -15),
      c("+" = 0,  "-" = 0)
    )

    # 6. A length-one list recycles to every sequence
    gene_label_rotation = list(20)

## 7. Layer reference

Layer | Function | Description
--- | --- | ---
Sequence arcs | `geom_seq()` | Draws an arc (or line) for each sequence, with direction arrows
Alignment ribbons | `geom_ribbon()` | Draws colored ribbons from alignment results
Gene arrows | `geom_gene()` | Draws gene/feature arrow polygons
Gene labels | `geom_gene_label()` | Draws gene labels at fixed positions
Repelled gene labels | `geom_gene_label_repel()` | ggrepel-style labels with leader lines, wrapping and overlap hiding
Axes | `geom_axis()` | Draws axis lines, major/minor ticks and tick labels
Sequence labels | `geom_seq_label()` | Places sequence names on/outside the arcs

## 8. Plot interpretation

- Sequence arcs — each colored arc is one sequence, with length proportional to the sequence length and arrows showing direction.
- Ribbons — colored regions connecting sequences represent aligned/ homologous intervals; color encodes identity, query or subject by default.
- Gene arrows — arrow polygons on the sequences; color encodes strand or functional category, with optional labels.
- Axes — ticks and numbers outside each arc label sequence positions.

## Further reading

- Function reference
- Version history (NEWS.md)
- Package homepage