Malcolm Barrett
Malcolm Barrett

Reputation: 23

Handle ggplot2 axis text face programmatically

(x-posted to community.rstudio.com)

I'm wondering if it's possible to change the axis text in ggplot2 programatically or if there is some native way to do this in ggplot2. In this reprex, the idea is that I want to bold the axis text of a variable y that has an absolute value of x over 1.5. I can add it in manually via theme(), and that works fine:


library(ggplot2)
library(dplyr)
library(forcats)

set.seed(2939)
df <- data.frame(x = rnorm(15), y = paste0("y", 1:15), group = rep(1:3, 5))

df <- mutate(df, big_number = abs(x) > 1.5, face = ifelse(big_number, "bold", 
  "plain"))

p <- ggplot(df, aes(x = x, y = fct_inorder(y), col = big_number)) + geom_point() + 
  theme(axis.text.y = element_text(face = df$face))

p

Plot 1 with no facets Plot 1 with no facets

But if I facet it by group, y gets reordered and ggplot2 has no idea how face is connected to df and thus y, so it just bolds in the same order as the first plot.

p + facet_grid(group ~ .)

Plot 2 with facets Plot 2 with facets

And it's worse if I use a different scale for each.

p + facet_grid(group ~ ., scales = "free")

Plot 3 with facets and different scales Plot 3 with facets and different scales

What do you think? Is there a general way to handle this that would work consistently here?

Upvotes: 2

Views: 538

Answers (2)

Malcolm Barrett
Malcolm Barrett

Reputation: 23

Inspired by @PoGibas, I also used a function in scale_y_discrete(), which works, too.

bold_labels <- function(breaks) {
  big_nums <- filter(df, y %in% breaks) %>%
    pull(big_number)
  labels <- purrr::map2(
    breaks, big_nums,
    ~ if (.y) bquote(bold(.(.x))) else bquote(plain(.(.x)))
  )
  parse(text = labels)
}

ggplot(df, aes(x, fct_inorder(y), col = big_number)) + 
    geom_point() +
    scale_y_discrete(labels = bold_labels) +
    facet_grid(group ~ ., scales = "free")

Upvotes: 0

pogibas
pogibas

Reputation: 28339

Idea: Don't change theme, change y-axis labels. Create a call for every y with if/else condition and parse it with parse.

Not the most elegant solution (using for loop), but works (need loop as bquote doesn't work with ifelse). I always get confused when trying to work with multiple expressions (more on that here).

Code:

# Create data
library(tidyverse)
set.seed(2939)
df <- data.frame(x = rnorm(15), y = paste0("y", 1:15), group = rep(1:3, 5)) %>%
    mutate(yF = fct_inorder(y),
           big_number = abs(x) > 1.5)

# Expressions for y-axis
# ifelse doesn't work
# ifelse(df$big_number, bquote(bold(1)), bquote(plain(2)))
yExp <- c() # Ignore terrible way of concatenating 
for(i in 1:nrow(df)) {
    if (df$big_number[i]) {
        yExp <- c(yExp, bquote(bold(.(as.character(df$yF[i])))))
    } else {
        yExp <- c(yExp, bquote(plain(.(as.character(df$yF[i])))))
    }
}

# Plot with facets
ggplot(df, aes(x, yF, col = big_number)) + 
    geom_point() +
    scale_y_discrete(breaks = levels(df$yF), 
                     labels = parse(text = yExp)) +
    facet_grid(group ~ ., scales = "free")

Result:

enter image description here

Upvotes: 1

Related Questions