Align data points with missing values in ggplot2 without faceting

Viewed 139

I have a dataframe that (very abstractly) looks like this. Note that not every taster rated every fruit.

df <- data.frame(
  fruit = c("apple", "apple", "apple", "banana", "banana", "banana"),
  taster = c("Ann", "Bob", "Don", "Bob", "Cat", "Don"),
  rating = c(8, 7, 4, 6, 3, 7)
)

I want to create two plots using ggplot2, one for apples and one for bananas. I want points indicating ratings, and the points need to be connected by lines. Crucially, even though Ann did not rate bananas and Cat did not rate apples, I still want both plots to have all four raters along the x-axis. In the apple plot, the point for Bob's rating should be connected directly to the point for Don's rating, skipping over the Cat column. In the banana plot, there will simply be three connected points for Bob, Cat, and Don; nothing will be in the Ann column.

I can accomplish this by faceting, like this:

this.

plot_facet <- ggplot(df, aes(x = taster, y = rating, col = fruit, group = fruit)) +
  geom_point() + geom_line() +
  scale_color_manual(values=c("#123456", "#abcdef")) +
  facet_grid(fruit ~ .)

For a complicated independent reason, though, I do not want to use faceting. Rather, I want to make two separate graphs and stitch them together with (e.g.) ggarrange(). But when I do that, this is the result:

The Ann column is omitted from the banana graph, and the Cat column from the apple graph. What I don't like about this is that it's difficult to compare visually a single person's ratings for the two fruits.

plot_apple <- ggplot(subset(df, fruit == "apple"), 
                     aes(x = taster, y = rating, col = fruit, group = fruit)) +
  geom_point() + geom_line() +
  scale_color_manual(values=c("#123456"))

plot_banana <- ggplot(subset(df, fruit == "banana"), 
                     aes(x = taster, y = rating, col = fruit, group = fruit)) +
  geom_point() + geom_line() +
  scale_color_manual(values=c("#abcdef"))

ggarrange(plot_apple, plot_banana, ncol = 1, nrow = 2)

How do I prevent this? I've tried adding rows with empty values to my data frame, but that doesn't help.

2 Answers

You can convert the names to factors then use drop = FALSE in a scale_x_discrete call. Here's a reprex:

library(ggplot2)
library(ggpubr)

df <- data.frame(
  fruit = c("apple", "apple", "apple", "banana", "banana", "banana"),
  taster = c("Ann", "Bob", "Don", "Bob", "Cat", "Don"),
  rating = c(8, 7, 4, 6, 3, 7)
)

df$taster <- factor(df$taster)

plot_apple <- ggplot(subset(df, fruit == "apple"), 
                     aes(x = taster, y = rating, col = fruit, group = fruit)) +
  geom_point() + geom_line() +
  scale_color_manual(values=c("#123456"))+
  scale_x_discrete(drop = FALSE)

plot_banana <- ggplot(subset(df, fruit == "banana"), 
                      aes(x = taster, y = rating, col = fruit, group = fruit)) +
  geom_point() + geom_line() +
  scale_color_manual(values=c("#abcdef")) +
  scale_x_discrete(drop = FALSE)

ggarrange(plot_apple, plot_banana, ncol = 1, nrow = 2)

Created on 2020-06-20 by the reprex package (v0.3.0)

Another, more hacky option. (I prefer Allan's solution, though!). This solution also requires to factorise the variable first!

The trick is to change the cowplot::plot_grid function as suggested in Z.Lin's great hack!

df <- data.frame(
  fruit = c("apple", "apple", "apple", "banana", "banana", "banana"),
  taster = c("Ann", "Bob", "Don", "Bob", "Cat", "Don"),
  rating = c(8, 7, 4, 6, 3, 7), stringsAsFactors = TRUE ## factorised
)

# trace(cowplot::plot_grid, edit = TRUE)
## following Z.Lin's suggesting to modify it only in the session. 
## Then I used the function `align_plots_modified()` from her post which I will not post again here

## The labels need some tweaking
plot_apple <- ggplot(subset(df, fruit == "apple"), 
                     aes(x = as.integer(taster), y = rating, col = fruit, group = fruit)) +
  scale_x_continuous(breaks = 1:4, labels = levels(df$taster)) + #added labels and breaks
  geom_point() + 
  geom_line() +
  scale_color_manual(values=c("#123456"))

plot_banana <- ggplot(subset(df, fruit == "banana"), 
                      aes(x = as.integer(taster), y = rating, col = fruit, group = fruit)) +
  geom_point() + 
  geom_line() +
  scale_x_continuous(breaks = 1:4, labels = levels(df$taster)) +
  scale_color_manual(values=c("#abcdef"))

cowplot::plot_grid(plot_apple, plot_banana, nrow = 2, align = "v")

# untrace(cowplot::plot_grid)

enter image description here

Related