8

I am trying to generate a report template in RMarkdown. In this report, I would like a plot in the cell next to the number and text of a question on the survey that I am using. I generated some example plots and made the following example in MS Word. Is it possible to put an R-generated plot in a table in RMarkdown?

What would the code look like if it is?

Desired output

Edit (9/8/16): I am including the .Rmd file as it stands now. The issue is that the html file fails to compile with the following message.

pandoc: Could not fetch Template1_files/figure-html/score_table-1.png
Template1_files/figure-html/score_table-1.png: openBinaryFile: does not exist (No such file or directory)
Error: pandoc document conversion failed with error 67
Execution halted

Template1 is the file name, score_table is the chunk label.

Would someone be willing to help me diagnose the problem?

<meta charset="utf-8">
---
title: "Untitled"
author: "Xander"
date: "September 7, 2016"
output: html_document
    self_contained: false
---

```{r mychunk, fig.show = "hide", echo = FALSE, fig.height=3, fig.width=5}
library(knitr)
library(ggplot2)

# sample data
dat <- data.frame(text = sapply(1:10, FUN = function(x) { paste0(sample(x = LETTERS, size = 15), collapse = "") }))
score_set = replicate(n = 10, expr = {data.frame(other = c("other", "other", "other", "other"), score=sample(1:7,4,TRUE))},simplify = F)

#Plot Function
plotgen<-function(score_set,other,score){
  p <- ggplot(score_set, aes(factor(other), score))
  p + geom_violin(fill = "#99CCFF") + coord_flip() + scale_x_discrete(name=NULL) +
  scale_y_continuous(breaks = round(seq(1, 7, by = 1),1), limits = c(1,7), name=NULL) + theme(axis.text.y=element_blank(),axis.title.y=element_blank(),axis.ticks.y=element_blank(),
      panel.grid.major.y = element_line(colour = "black"),
      panel.grid.minor = element_blank(),
      panel.background = element_rect(fill = "white"),
      panel.border = element_rect(colour = "black", fill=NA, size=1)) +
geom_hline(yintercept=sample(1:7,1,TRUE), size = 1.5, colour = "#334466")
}

# generate plots
invisible(lapply(seq_along(score_set), FUN = function(x) {plotgen(score_set[[x]],other,score)}))

out <- cbind(row.names(dat), 
         as.character(dat$text), 
         sprintf("![](%s%s-%s.png)", opts_current$get("fig.path"), opts_current$get("label"), 1:nrow(dat)))
kable(out, col.names = c("ID", "Text", ""))
````
Xander
  • 593
  • 1
  • 5
  • 19

2 Answers2

6

You could do the following (for base plots, not for ggplot2 plots – see comment at the very bottom):

```{r mychunk, fig.show = "hide", echo = FALSE, fig.height=3, fig.width=5}
library(knitr)
# sample data
dat <- data.frame(
  text = sapply(1:10, FUN = function(x) { paste0(sample(x = LETTERS, size = 15), collapse = "") }), 
  x1 = rnorm(10), 
  x2 = rnorm(10, mean = 3), 
  x3 = rnorm(10, mean = 5))

# generate plots
invisible(apply(dat[, 2:4], MARGIN = 1, FUN = boxplot))

out <- cbind(row.names(dat), 
             as.character(dat$text), 
             sprintf("![](%s%s-%s.png)", opts_current$get("fig.path"), opts_current$get("label"), 1:nrow(dat)))
kable(out, col.names = c("ID", "Text", "Boxplot"))
```
  • apply(dat[, 2:4], MARGIN = 1, FUN = boxplot) generates boxplots using the data from x1, x2 and x3. The figures are produced but not included into the document because of fig.show="hide".
  • sprintf("![](%s%s-%s.png)", opts_current$get("fig.path"), opts_current$get("label"), 1:nrow(dat)) generates the markdown syntax to include the plots. This is similar to calling include_graphics but has the advantage that we get the markdown as character vector.
  • Finally, kable produces the table.

Alternatively, you could produce the table manually, using Pandoc's pipe_table as shown here which gives some more flexibility.

Output of the code above:

Figure in table

Gregor's answer here shows how to apply this to ggplot2 plots: Print the list returned by apply element by element, i.e. invisible(apply(...)) becomes invisible(lapply(apply(...), print)).

Community
  • 1
  • 1
CL.
  • 14,577
  • 5
  • 46
  • 73
  • Thank you for this very helpful response. This looks very promising. I had a clarifying question on how paths to undisplayed images are generated in RMarkdown. After replacing the boxplot function with the function I use to generate my graph in ggplot2, the following error occurred: `pandoc: Could not fetch Template1_files/figure-html/mychunk-1.png` Is the path to ggplot2 plots different than base package plots? Would I have to specify it differently when using `sprintf()`? Again, thank you for your help. – Xander Sep 07 '16 at 22:33
  • 1
    No, ggplot should not matter. But you can set [`self_contained: false`](http://rmarkdown.rstudio.com/html_document_format.html#document_dependencies) and check the file path after compiling. Of course, you need to replace `mychunk` with the label of the chunk where the plot has been generated. – CL. Sep 08 '16 at 06:06
  • Thank you for your response. I did try checking the file path before, but did not do so with `self_contained: false`. Unfortunately, `file.exists(paste(getwd(),"/Template1_files/figure-html/score_table-1.png",sep = ""))` still returns as false after compiling. (The chunk's label is 'score_table'.) I double-checked that the tables appear when generating them without `invisible()`. Do you have another recommendation to help diagnose the problem? – Xander Sep 08 '16 at 10:22
  • 1
    Sorry, my previous comment was wrong: ggplot *does* matter. The problem is, ggplots are only generated if they are *printed*. Replacing `invisible` in your updated code with `print` solves that issue. However, this clutters the document with unwanted output. At the moment, I don't know how to avoid this. Please consider asking a new question about how to produce ggplots in a loop and generating the files *without* printing any visible output. This seems to be a very special issue, mostly unrelated to your original question. – CL. Sep 08 '16 at 19:12
  • Related: [Yihui's answer here](http://stackoverflow.com/a/35194138/2706569), http://stackoverflow.com/a/37019205/2706569 – CL. Sep 08 '16 at 19:17
0

With ggplot2 and the gt package, this is now possible.

library(ggplot2)
library(gt)

# Create a ggplot plot
plot_object <-
  ggplot(
    data = gtcars,
    aes(x = hp, y = trq,
        size = msrp)) +
  geom_point(color = "blue") +
  theme(legend.position = "none")

# Create a tibble that contains two
# cells (where one is a placeholder for
# an image), then, create a gt table;
# use the `text_transform()` function
# to insert the plot using by calling
# `ggplot_object()` within the user-
# defined function
tab_1 <-
  dplyr::tibble(
    text = "Here is a ggplot:",
    ggplot = NA
  ) %>%
  gt() %>%
  text_transform(
    locations = cells_body(columns = ggplot),
    fn = function(x) {
      plot_object %>%
        ggplot_image(height = px(200))
    }
  )

Source[21/01/2022]: https://gt.rstudio.com/reference/ggplot_image.html

Community
  • 1
  • 1
understorey
  • 124
  • 1
  • 10