R ggplot2 - Add Labels, Title, Annotations

The `labs()` function provides the most straightforward approach to adding labels in ggplot2. It handles titles, subtitles, captions, and axis labels in a single function call.

Key Insights

  • ggplot2 provides a layered system for adding text elements through labs(), ggtitle(), xlab(), ylab(), and annotate() functions that work independently or together
  • Annotations differ from labels by placing text at specific coordinate positions, enabling detailed plot commentary using annotate(), geom_text(), and geom_label()
  • Advanced customization through theme() allows precise control over text appearance, positioning, and styling for publication-ready visualizations

Basic Labels and Titles

The labs() function provides the most straightforward approach to adding labels in ggplot2. It handles titles, subtitles, captions, and axis labels in a single function call.

library(ggplot2)

# Create sample dataset
data <- data.frame(
  x = 1:10,
  y = rnorm(10, mean = 5, sd = 2),
  category = rep(c("A", "B"), each = 5)
)

# Basic plot with comprehensive labels
ggplot(data, aes(x = x, y = y, color = category)) +
  geom_point(size = 3) +
  geom_line() +
  labs(
    title = "Performance Metrics Over Time",
    subtitle = "Comparing Category A and B",
    x = "Time Period",
    y = "Performance Score",
    color = "Category",
    caption = "Source: Internal Analytics Database"
  )

Individual label functions offer granular control when you need to add or modify specific elements:

ggplot(data, aes(x = x, y = y)) +
  geom_point() +
  ggtitle("Main Title", subtitle = "Additional Context") +
  xlab("Independent Variable") +
  ylab("Dependent Variable")

Mathematical Expressions in Labels

ggplot2 supports mathematical notation through R’s expression() function, essential for scientific and technical visualizations.

# Plot with mathematical expressions
ggplot(data.frame(x = seq(0, 10, 0.1)), aes(x = x)) +
  stat_function(fun = function(x) x^2) +
  labs(
    title = expression(paste("Function: ", f(x) == x^2)),
    x = expression(alpha + beta),
    y = expression(sqrt(x + y))
  )

# Greek letters and complex formulas
ggplot(data, aes(x = x, y = y)) +
  geom_point() +
  labs(
    x = expression(mu[x] * " (mean)"),
    y = expression(sigma^2 * " (variance)"),
    title = expression(paste("Relationship between ", mu, " and ", sigma^2))
  )

Point Annotations with geom_text and geom_label

Use geom_text() to add text labels directly on the plot area, typically to label specific data points.

# Label specific points
data$label <- ifelse(data$y > 6, paste0("Point ", data$x), "")

ggplot(data, aes(x = x, y = y)) +
  geom_point(size = 3) +
  geom_text(aes(label = label), 
            vjust = -0.5, 
            hjust = 0.5,
            size = 3.5)

# Using geom_label for boxed text
ggplot(data, aes(x = x, y = y, color = category)) +
  geom_point(size = 3) +
  geom_label(aes(label = round(y, 1)),
             vjust = -0.5,
             size = 3,
             label.padding = unit(0.15, "lines"))

The ggrepel package prevents label overlap automatically:

library(ggrepel)

# Create crowded data
crowded_data <- data.frame(
  x = rnorm(50),
  y = rnorm(50),
  label = paste0("P", 1:50)
)

ggplot(crowded_data, aes(x = x, y = y, label = label)) +
  geom_point() +
  geom_text_repel(
    size = 3,
    max.overlaps = 20,
    box.padding = 0.5,
    point.padding = 0.3
  )

Custom Annotations with annotate()

The annotate() function places text, shapes, or segments at specific coordinates without mapping to data.

# Text annotations at fixed positions
ggplot(data, aes(x = x, y = y)) +
  geom_point() +
  annotate("text", 
           x = 3, 
           y = 8, 
           label = "Peak Performance",
           size = 4,
           color = "red",
           fontface = "bold") +
  annotate("text",
           x = 7,
           y = 2,
           label = "Low Point",
           size = 4,
           color = "blue")

# Rectangle annotation for highlighting regions
ggplot(data, aes(x = x, y = y)) +
  geom_line() +
  annotate("rect",
           xmin = 4, xmax = 6,
           ymin = -Inf, ymax = Inf,
           alpha = 0.2,
           fill = "yellow") +
  annotate("text",
           x = 5, y = 8,
           label = "Critical Period",
           fontface = "italic")

Arrow Annotations and Segments

Combine annotations with arrows to point out specific features:

# Arrow pointing to specific data point
max_point <- data[which.max(data$y), ]

ggplot(data, aes(x = x, y = y)) +
  geom_line() +
  geom_point() +
  annotate("segment",
           x = max_point$x + 1.5,
           xend = max_point$x + 0.2,
           y = max_point$y + 1,
           yend = max_point$y + 0.1,
           arrow = arrow(length = unit(0.3, "cm")),
           color = "red",
           size = 1) +
  annotate("text",
           x = max_point$x + 1.5,
           y = max_point$y + 1.2,
           label = sprintf("Maximum: %.2f", max_point$y),
           hjust = 0,
           color = "red")

Multi-line Annotations

For longer annotations, use \n for line breaks or the paste() function:

ggplot(data, aes(x = x, y = y)) +
  geom_point() +
  annotate("text",
           x = 2,
           y = 8,
           label = "Multi-line annotation\nLine 2\nLine 3",
           size = 3.5,
           lineheight = 0.9,
           hjust = 0) +
  annotate("label",
           x = 8,
           y = 2,
           label = paste("Boxed text", "with multiple lines", sep = "\n"),
           size = 3)

Theme Customization for Text Elements

The theme() function provides extensive control over all text elements:

ggplot(data, aes(x = x, y = y, color = category)) +
  geom_point(size = 3) +
  labs(
    title = "Customized Text Elements",
    subtitle = "Complete theme control",
    x = "X Axis",
    y = "Y Axis"
  ) +
  theme(
    plot.title = element_text(
      size = 16,
      face = "bold",
      hjust = 0.5,
      color = "#2C3E50"
    ),
    plot.subtitle = element_text(
      size = 12,
      hjust = 0.5,
      color = "#7F8C8D",
      margin = margin(b = 10)
    ),
    axis.title.x = element_text(
      size = 12,
      face = "bold",
      margin = margin(t = 10)
    ),
    axis.title.y = element_text(
      size = 12,
      face = "bold",
      margin = margin(r = 10)
    ),
    axis.text = element_text(
      size = 10,
      color = "#34495E"
    ),
    legend.title = element_text(
      size = 11,
      face = "bold"
    ),
    legend.text = element_text(size = 10)
  )

Dynamic Labels from Data

Generate labels programmatically based on data characteristics:

# Calculate statistics for labels
library(dplyr)

summary_stats <- data %>%
  group_by(category) %>%
  summarize(
    mean_y = mean(y),
    max_x = max(x)
  )

ggplot(data, aes(x = x, y = y, color = category)) +
  geom_point() +
  geom_text(
    data = summary_stats,
    aes(x = max_x, y = mean_y, 
        label = sprintf("Mean: %.2f", mean_y)),
    hjust = 1.1,
    size = 3.5,
    show.legend = FALSE
  )

Facet Labels

Customize facet labels using labeller():

# Custom facet labels
data$facet_var <- rep(c("group_1", "group_2"), length.out = nrow(data))

ggplot(data, aes(x = x, y = y)) +
  geom_point() +
  facet_wrap(
    ~facet_var,
    labeller = labeller(
      facet_var = c(
        "group_1" = "First Group",
        "group_2" = "Second Group"
      )
    )
  ) +
  theme(strip.text = element_text(size = 12, face = "bold"))

This comprehensive approach to labels and annotations transforms basic plots into informative visualizations. Combine these techniques based on your specific requirements—use labs() for standard labeling, annotate() for commentary, and theme() for precise styling control. The layered nature of ggplot2 allows incremental refinement until you achieve the exact presentation needed for your audience.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.