Why coord_serial instead of standard faceting?

One way to plot genomic data in ggplot2 is using facet_grid(). However, for complex plots like a Manhattan plot, this approach leads to a fragmented visualization and layering issues.

The facet_grid Approach

To get proportional widths for chromosomes, you must use facet_grid(. ~ chrom, scales = "free_x", space = "free_x"). Even with panel.spacing = unit(0, "lines"), there is not consistent spacing between chromosomes and even with coord_cartesian(clip = "off"), labels don’t span multiple regions cleanly.

library(ggplot2)
library(coord.serial)

target_hit <- simulated_gwas[simulated_gwas$chrom == "17" & simulated_gwas$causal, ][1, ]
target_hit$label <- "rs9823673"

ggplot(simulated_gwas, aes(x = position, y = log10p)) +
    geom_point(aes(color = causal)) +
    geom_text(data = target_hit, aes(label = label), vjust = -1) +
    geom_hline(yintercept = -log10(5e-8), color = "red", linetype = "dashed") +
    facet_grid(. ~ chrom, scales = "free_x", space = "free_x", switch = "x") +
    coord_cartesian(clip = "off") + # Attempt to prevent label clipping
    theme_minimal() +
    theme(
        panel.spacing = unit(0, "lines"),
        strip.placement = "outside",
        panel.grid.minor = element_blank()
    )

Manhattan facet_grid

The coord_serial Approach

p <- ggplot(simulated_gwas, aes(x = position, y = log10p, domain = chrom, color = causal)) +
    geom_point() +
    geom_text(data = target_hit, aes(label = label), vjust = -1) +
    geom_hline(yintercept = -log10(5e-8), color = "red", linetype = "dashed") +
    coord_serial() +
    theme_minimal()

Manhattan coord_serial

Key Advantages of coord_serial()

1. Unified Canvas (Zero Artifacts)

In facet_grid, even if you use clip = "off", labels that cross into adjacent panels face z-index issues. Because facets are drawn one by one, a label from Chromosome 17 is drawn behind the background and grid lines of Chromosome 18 (\(rs9...\) in the plot above).

In coord_serial(), the entire plot is a single panel, so labels and lines span boundaries perfectly.

2. Seamless Axis

Because coord_serial() maps multiple domains to a single continuous x-axis, the axis line is unbroken. Faceting creates a “comb” effect with 23 independent axes that are difficult to style consistently.

3. Logical Coordinates

You can add annotations (like geom_rect or geom_segment) across multiple chromosomes using a single x-scale. With faceting, you are logically restricted to one panel at a time.

4. Minimal Code

coord_serial() automatically handles proportional scaling and domain gaps. You don’t need to remember complex facet_grid arguments like scales = "free_x" and space = "free_x".