The Challenge: Aligning Nested Labels in Horizontal Chicklet Charts

Creating highly customized, publication-ready visualizations in R often requires combining specialized packages. A common hurdle arises when pairing ggchicklet (used for elegant, rounded-corner bar plots) with legendry (used to create nested axis brackets like guide_axis_nested).

When creating a horizontal bar plot, many developers historically relied on coord_flip(). However, using coord_flip() with nested axes often breaks label alignment, leaving text shifted or uncentered. This happens because legendry relies on native coordinate systems to calculate nested bracket positioning, and coordinate flipping disrupts this layout engine.

The Root Cause

In older versions of ggplot2, coord_flip() was the only way to make bar charts horizontal. Today, ggplot2 natively supports horizontal orientation by swapping the x and y aesthetic mappings.

While some older documentation suggests geom_chicklet doesn't support swapped aesthetics, modern versions of ggchicklet fully support native horizontal rendering. By mapping your categorical variables directly to y and your numeric values to x, you can completely eliminate the need for coord_flip(). This allows legendry to calculate bracket positions accurately.

The Solution: Native Aesthetic Mapping

To resolve the alignment issue, we will map our variables natively and remove coord_flip(). Here is the corrected, optimized code utilizing your dataset:

library(ggplot2)
library(ggchicklet)
library(legendry)
library(ggpubr)
library(ggsci)

# Your provided dataset
excel <- structure(list(
  variant_type = structure(c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L), 
                           levels = c("all", "SNVs", "INDELs"), class = "factor"), 
  sample = structure(c(1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L), 
                     levels = c("clone0_24h", "clone4_12w", "clone5_12w", "clone7_12w"), class = "factor"), 
  count = c(3632307, 3610039, 3639793, 3627187, 2947134, 2962191, 2935353, 2934755, 684923, 647589, 704196, 692176, 2351557, 2322460, 2383840, 2347099, 1794417, 1809360, 1792501, 1776124, 556995, 512975, 591205, 570849), 
  reference = c("CHM13", "CHM13", "CHM13", "CHM13", "CHM13", "CHM13", "CHM13", "CHM13", "CHM13", "CHM13", "CHM13", "CHM13", "hap1", "hap1", "hap1", "hap1", "hap1", "hap1", "hap1", "hap1", "hap1", "hap1", "hap1", "hap1"), 
  row.names = c(NA, -24L), class = c("tbl_df", "tbl", "data.frame"))

# Plotting with swapped aesthetics directly
ggplot(excel, aes(y = interaction(sample, reference), x = count, fill = variant_type)) +
  theme_classic2() +
  # ggplot2 automatically detects horizontal orientation based on x/y mapping
  geom_chicklet(position = "dodge2", radius = grid::unit(1.5, "mm")) +
  facet_wrap(~reference, strip.position = "left", ncol = 1, scales = "free_y") +
  theme(
    legend.title = element_text(face = 'italic', size = 16),
    legend.position = 'bottom',
    panel.grid = element_blank(),
    axis.text.y = element_text(face = 'bold'),
    axis.title.x = element_blank(),
    axis.title.y = element_blank(),
    strip.background.y = element_blank(),
    strip.text.y = element_blank(),
    legend.text = element_text(size = 12),
    axis.title = element_text(size = 14),
    axis.text = element_text(size = 10)
  ) +
  guides(
    # Adjusting angle to 0 degrees keeps nested labels readable horizontally
    y = guide_axis_nested(
      levels_text = list(element_text(angle = 0, hjust = 0.5), element_text()),
      levels_brackets = list(element_line(linewidth = .25), element_line(linewidth = .125))
    ),
    fill = guide_legend(
      title = 'variants',
      title.position = 'top',
      title.hjust = .5,
      position = "bottom"
    )
  ) +
  scale_fill_npg() +
  xlab(NULL) +
  ylab(NULL)

Key Adjustments Explained

  • Removed coord_flip(): We swapped the aesthetics directly within the aes() call (y = interaction(...) and x = count).
  • Native Orientation: Modern versions of geom_chicklet automatically detect the horizontal orientation when numerical data is mapped to the X-axis and categorical data to the Y-axis.
  • Text Angle Adjustment: Changed the Y-axis nested text angle from 90 to 0 within guide_axis_nested(). Since we are no longer flipping coordinates post-rendering, keeping the text at 0 degrees ensures it reads perfectly from left to right next to the brackets.