Home > Enterprise >  Table smaller than plot in ggplot
Table smaller than plot in ggplot

Time:11-30

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

CodePudding user response:

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

CodePudding user response:

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 |> 
  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(~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!

  • Related