2
votes

I'm looking to create a plot that contains a simple multi-line ggplot2 graph with a table of separate (but relevant) data below the graph that lines up by the X axis of the graph. The data table's column names do match the x axis of the graph (hours 1 to 24), but one column is dedicated to necessary row names.

Here are the graph and the data table separately: ggplot2 graph

Data table is cut off at hour 16 for brevity, but does extend to 24. Data Table

I've been attempting different solutions in gridExtra all morning adjusting different parameters like nrow, ncol, heights and widths, but the most simple solution is only one that produces a somewhat reasonable result. The code and image below is the best I have achieved:

library(gridExtra)

p1 <- ggplot(load_forecast_plot, aes(group=Load_Type, y=Load_Values, x=Hour,  colour = Load_Type)) + 
  geom_line(size = 1) +
  scale_x_continuous(breaks = c(1:24))

p1 <- ggplotGrob(p1)

p2<-tableGrob(df)

grid.arrange(p1, p2, top = paste("Load and Weather Error Power Grid", Sys.Date()-1, sep = " "))

grid.draw(tableGrob(MISO_wx_PrevDay_error_test,theme=ttheme_minimal(base_size = 5)))

Which produces: enter image description here

I would like the graph to be larger while the table is smaller and aligned along the x axis as much as possible. I've looked into examples where the table is converted to a ggplot2 object, but those examples have data that is the same in the plot and table unlike mine.

Below is my data for a reproducible example. Any help is much appreciated! Thank you.

data for the ggplot graph:

dput(load_forecast_plot)
structure(list(Hour = c(1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 
5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 
11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 16, 
17, 17, 17, 18, 18, 18, 19, 19, 19, 20, 20, 20, 21, 21, 21, 22, 
22, 22, 23, 23, 23, 24, 24, 24), Load_Type = c("Load", "DA_MTLF", 
"BC_MTLF", "Load", "DA_MTLF", "BC_MTLF", "Load", "DA_MTLF", "BC_MTLF", 
"Load", "DA_MTLF", "BC_MTLF", "Load", "DA_MTLF", "BC_MTLF", "Load", 
"DA_MTLF", "BC_MTLF", "Load", "DA_MTLF", "BC_MTLF", "Load", "DA_MTLF", 
"BC_MTLF", "Load", "DA_MTLF", "BC_MTLF", "Load", "DA_MTLF", "BC_MTLF", 
"Load", "DA_MTLF", "BC_MTLF", "Load", "DA_MTLF", "BC_MTLF", "Load", 
"DA_MTLF", "BC_MTLF", "Load", "DA_MTLF", "BC_MTLF", "Load", "DA_MTLF", 
"BC_MTLF", "Load", "DA_MTLF", "BC_MTLF", "Load", "DA_MTLF", "BC_MTLF", 
"Load", "DA_MTLF", "BC_MTLF", "Load", "DA_MTLF", "BC_MTLF", "Load", 
"DA_MTLF", "BC_MTLF", "Load", "DA_MTLF", "BC_MTLF", "Load", "DA_MTLF", 
"BC_MTLF", "Load", "DA_MTLF", "BC_MTLF", "Load", "DA_MTLF", "BC_MTLF"
), Load_Values = c(59141, 59260, 57862, 56493, 56470, 54964, 
54480, 54553, 52996, 53270, 53252, 51683, 53050, 52520, 50845, 
53020, 51723, 49627, 53844, 51907, 49293, 56956, 55069, 52700, 
60975, 60036, 58251, 65595, 65023, 63881, 69796, 69023, 68776, 
73392, 72517, 72591, 76412, 74896, 75452, 78454, 76538, 77547, 
79959, 77782, 79256, 81315, 78851, 80627, 82478, 79921, 81763, 
82638, 80027, 81896, 81244, 78906, 80328, 78484, 76627, 77304, 
77187, 75130, 75391, 74495, 72612, 72776, 69736, 68216, 68488, 
64844, 63756, 64145)), row.names = c(NA, -72L), class = c("tbl_df", 
"tbl", "data.frame"))

data table:

dput(df)
structure(list(WX_Error = c("CloudCover", "DewPoint", "RainFall", 
"SolarRadiation", "Temperature", "WindSpeed"), `1` = c("-13.72%", 
"-0.41°F", "0in", "0min", "-0.86°F", "0.26mph"), `2` = c("-8.52%", 
"-0.05°F", "-0.01in", "0min", "-1.2°F", "-0.11mph"), `3` = c("-9.22%", 
"-0.41°F", "-0.01in", "0min", "-1.26°F", "-1.41mph"), `4` = c("-14.57%", 
"-0.98°F", "-0.01in", "0min", "-1.48°F", "-0.99mph"), `5` = c("-15.81%", 
"-0.83°F", "-0.01in", "0min", "-0.83°F", "-1.58mph"), `6` = c("-13.43%", 
"-0.61°F", "0in", "-0.43min", "-0.46°F", "0.48mph"), `7` = c("-14.23%", 
"-0.28°F", "0in", "7.91min", "-1.15°F", "-0.43mph"), `8` = c("-2.29%", 
"0.1°F", "0in", "1.3min", "-0.72°F", "0.51mph"), `9` = c("-3.63%", 
"0.2°F", "0in", "1.96min", "-0.94°F", "-0.9mph"), `10` = c("4.73%", 
"0.25°F", "0in", "-2.99min", "-0.69°F", "0.25mph"), `11` = c("-8.68%", 
"0.8°F", "0.01in", "5.03min", "-0.83°F", "0.81mph"), `12` = c("-4.42%", 
"0.64°F", "0.01in", "2.34min", "-0.3°F", "0.9mph"), `13` = c("-15.06%", 
"0.49°F", "-0.01in", "8.08min", "0.29°F", "0.44mph"), `14` = c("-25.35%", 
"0.55°F", "-0.01in", "14.4min", "0.47°F", "0.59mph"), `15` = c("-19.36%", 
"0.6°F", "-0.01in", "10.76min", "0.44°F", "1.29mph"), `16` = c("-8.1%", 
"0.17°F", "-0.01in", "5.03min", "0.29°F", "1.26mph"), `17` = c("-21.01%", 
"-0.27°F", "-0.01in", "11.74min", "1.52°F", "0.72mph"), `18` = c("-22.84%", 
"-0.74°F", "-0.01in", "12.77min", "2.17°F", "1.34mph"), `19` = c("-18.57%", 
"-0.55°F", "0in", "10.35min", "0.46°F", "1.13mph"), `20` = c("-10.39%", 
"-0.91°F", "0.03in", "5.6min", "0.65°F", "0.71mph"), `21` = c("-6.65%", 
"-0.28°F", "0.06in", "1.66min", "-0.5°F", "-0.56mph"), `22` = c("-0.2%", 
"-0.4°F", "-0.01in", "0min", "-0.33°F", "-1.35mph"), `23` = c("4.39%", 
"0.11°F", "-0.01in", "0min", "-0.5°F", "-0.47mph"), `24` = c("-5.65%", 
"0.64°F", "0.01in", "0min", "-0.43°F", "0.35mph")), row.names = c(NA, 
-6L), groups = structure(list(Date = structure(c(18407, 18407, 
18407, 18407, 18407, 18407), class = "Date"), wx_vars = c("CloudCover", 
"DewPoint", "RainFall", "SolarRadiation", "Temperature", "WindSpeed"
), .rows = list(1L, 2L, 3L, 4L, 5L, 6L)), row.names = c(NA, -6L
), class = c("tbl_df", "tbl", "data.frame"), .drop = FALSE), class = c("grouped_df", 
"tbl_df", "tbl", "data.frame"))
2

2 Answers

8
votes

You can format the table as a ggplot object and then use the patchwork package to take care of the alignment for you.

library(ggplot2)
library(patchwork)

p1 <- ggplot(load_forecast_plot, aes(group=Load_Type, y=Load_Values, x=Hour,  colour = Load_Type)) + 
  geom_line(size = 1) +
  scale_x_continuous(breaks = c(1:24))

p2 <- gridExtra::tableGrob(df)
# Set widths/heights to 'fill whatever space I have'
p2$widths <- unit(rep(1, ncol(p2)), "null")
p2$heights <- unit(rep(1, nrow(p2)), "null")

# Format table as plot
p3 <- ggplot() +
  annotation_custom(p2)

# Patchwork magic
p1 + p3 + plot_layout(ncol = 1)

enter image description here

I know it doesn't look great right now; you'd have to tinker with the device size and text size a bit more. But, the question was about the alignment and that seems OK.

EDIT:

You can match up the axis ticks with the columns too if you set the x-axis correctly:

p1 <- ggplot(load_forecast_plot, aes(group=Load_Type, y=Load_Values, x=Hour,  colour = Load_Type)) + 
  geom_line(size = 1) +
  scale_x_continuous(breaks = c(1:24),
                     limits = c(-1, 24),
                     expand = c(0,0.5))

or you could set the second column as axis text:

p1 <- ggplot(load_forecast_plot, aes(group=Load_Type, y=Load_Values, x=Hour,  colour = Load_Type)) + 
  geom_line(size = 1) +
  scale_x_continuous(breaks = c(1:24),
                     expand = c(0,0.5))

p2 <- gridExtra::tableGrob(df)[, -c(1:2)]
p2$widths <- unit(rep(1, ncol(p2)), "null")
p2$heights <- unit(rep(1, nrow(p2)), "null")

p3 <- ggplot() +
  annotation_custom(p2) +
  scale_y_discrete(breaks = rev(df$WX_Error), 
                   limits = c(rev(df$WX_Error), ""))

p1 + p3 + plot_layout(ncol = 1)

EDIT2:

I also didn't see any text size options, but here is how you could change the font size manually:

is_text <- vapply(p2$grobs, inherits, logical(1), "text")
p2$grobs[is_text] <- lapply(p2$grobs[is_text], function(text) {
  text$gp$fontsize <- 8
  text
})
1
votes

After a bit of fiddling around I got to this:

library(ggpubr)
p1 <- ggplot(load_forecast_plot, aes(group=Load_Type, y=Load_Values, x=Hour,  colour = Load_Type)) + 
  geom_line(size = 1) +
  scale_x_continuous(breaks = c(1:24)) +
  theme(legend.position="top")+
  theme(plot.margin=unit(c(0,1,0,3.1),"cm"))
p1


p2 <- ggtexttable(df)

plot <- ggarrange(p1, p2,
                  ncol = 1, nrow = 2,
                  heights = c(10,3))
plot

Just play around with the margin and the device size until it is more or less aligned. Is a bit of a workaround though and @teunbrand's solution that I just saw now looks more promising.

enter image description here