3

I have been trying to find a solution to this problem for a little while now and all the answers don't seem to be quite what I'm looking for.

I'm sure the answer to this is probably simple and I'm overthinking it.

I've been trying to have a table next to a barplot which corresponds to the same observations in the table. However, the table doesn't seem to line up with the size of the plot because it has too much white space or is too small.

Is there a way that I can have the title of the plot and the title of the columns in the table lineup?

data(mtcars)
library(ggplot2)
library(dplyr)
library(grid)
library(gridExtra)
library(cowplot)

data <- mtcars %>% select(mpg, disp, cyl, qsec) %>% tibble::rownames_to_column("Car Name") %>% slice(1:7)

data$`Car Name` <- factor(data$`Car Name`, levels = data$`Car Name`)

t <- tableGrob(data %>% slice(1:7) %>% select(-mpg), 
               theme = ttheme_minimal(), 
               rows = NULL)
plot(t)

p <- ggplot(data = data, aes(x = mpg, y = `Car Name`)) + 
  geom_bar(stat = "identity", fill = "white", color = "black", alpha = 0.3, size = .75) + theme_classic() +
  theme(axis.text.y = element_blank(),
        axis.title.y = element_blank(),
        axis.title.x = element_blank(),
        plot.title =  element_text(face = "bold")) +
  ggtitle("No. of mpg") +
  scale_x_continuous(expand = expansion(mult = c(0, .1)), limits = c(0,30)) +
  scale_y_discrete(limits=rev)
p

grid.arrange(t, p, nrow = 1)

This is what I have done to make the table and plot. I have a basic grid.arrange at the bottom to highlight my issue.

The image here highlights the differences in size between the table and the plot enter image description here

2 Answers 2

3
library(dplyr); library(gt); library(gtExtras)
mtcars %>%
  tibble::rownames_to_column("Car Name") %>%
  select(1, disp, cyl, qsec, mpg) %>%
  slice(1:7) %>%
  gt() %>%
  gt_plt_bar(column = mpg, color = "gray80", scale_type = "number", 
             text_color = "gray30")

enter image description here

Alternatively, we could use gt::ggplot_image to create custom plots and put them in each row. (This took me a fair bit of fiddling and it's still hard to get the aspect ratios where I wanted them.)

library(tidyverse); library(gt)
plot_mpg <- function(df) {
  ggplot(data = df, aes(mpg, "a")) +
    geom_col(orientation = "y", fill = NA, color = "gray20", size = 4) +
    geom_text(aes(label = mpg), hjust = 1.1, size = 60) +
    coord_fixed(ratio = 5, xlim = c(0, 30)) +
    theme_void()
}

mtcars %>%
  tibble::rownames_to_column("Car Name") %>%
  nest(data = mpg) %>%
  mutate(plot = map(data, plot_mpg)) %>%
  select(1, disp, cyl, qsec, plot) %>%
  mutate(mpg = NA) %>% # placeholder column
  slice(1:7) -> a

gt(a) %>%
  cols_width(mpg ~ px(80)) %>%
  tab_options(data_row.padding = px(2)) %>%
  text_transform(
    locations = cells_body(mpg),
    fn = function(x) { map(a$plot, ~ggplot_image(., height = px(20), aspect_ratio = 6)) }
  ) %>%
  cols_hide(plot)
  

enter image description here

Sign up to request clarification or add additional context in comments.

3 Comments

I think this is a nice solution as well even though it wasn't exactly what I was looking for!
Good news is it's pre-built so very simple to implement; bad news is you lose some customizability.
I quite like this one as well - will be going in the back pocket (and I do love the gt package!)
2

The simplest way I've found in most posts before is to insert the text as another plot rather than a table, and use patchwork to line them up:

library(tidyverse)
library(patchwork)

data <- mtcars %>% select(mpg, disp, cyl, qsec) %>% rownames_to_column("Car Name") %>% slice(1:7)

data$order <- factor(as.integer(factor(data$`Car Name`)))

data_rs <- data |> 
  select(order, disp, cyl, qsec, `Car Name`) |> 
  pivot_longer(-order, names_to = "var", values_to = "val", values_transform = as.character)

p1 <- ggplot(data = data, aes(x = mpg, y = order)) + 
  geom_bar(stat = "identity", fill = "white", color = "black", alpha = 0.3, linewidth = .75) +
  theme_classic() +
  theme(axis.text.y = element_blank(),
        axis.title.y = element_blank(),
        axis.title.x = element_blank(),
        strip.background = element_blank(),
        strip.text =  element_text(face = "bold", size = 12, hjust = 0)) +
  scale_x_continuous(expand = expansion(mult = c(0, .1)), limits = c(0,30)) +
  scale_y_discrete(limits=rev) +
  facet_wrap(~"No. of mpg")

p2 <- ggplot(data_rs, (aes(x = 1, y = order, label = val))) +
  geom_text() +
  facet_wrap(~fct_inorder(var), nrow = 1) +
  scale_y_discrete(limits=rev) +
  theme(axis.title.y = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks = element_blank(),
        axis.text.x = element_blank(),
        axis.title.x = element_blank(),
        panel.grid = element_blank(),
        strip.background = element_blank(),
        panel.background = element_blank(),
        strip.clip = "off",
        strip.text =  element_text(face = "bold", size = 12)) +
  coord_cartesian(clip = "off")

p2 + p1 + plot_layout(widths = c(0.7, 0.3))

This first pivots your dataframe, then uses each variable column you want to position as a facet, with the column name at the top. You can play around with display using the theme elements to tidy up a bit, but hopefully a helpful way to do this neatly and quickly!

5 Comments

This is exactly what I was looking for thank you! I thought gridextra was going to be the answer but this looks much nicer I believe. Is there a way to add Car Name as another column on this table and remove the text on the left?
Just updated to leave the Car Name column in, doing this by creating an anonymous, ordered order factor column before splitting datasets. Also turned clipping off for text as the names were sometimes getting cut short. Hope that helps!
If the facet sizes and plot sizes are getting fiddly, using the similar principle of creating a pseudo-table using ggplot2 text can be helpful too - I have used this approach for forest plots in publications before.
Another thing I have noticed is that these column titles are in alphabetical order. If I wanted Car Name to be last how would I go about editing the order of the columns?
Simplest way would be to order the columns in your data before pivoting, then using facet_wrap(~fct_inorder(var)) to tell ggplot to put the facets in that order. Have done that in edited answer above :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.