I want to create a PDF file with several instances of two side-by-side flextables. For this, I want to be able to use a loop, since I want all different flextables to have different input, provided through the loop. To be able to put two flextables side-by-side, I am using minipages, for which I have to use multiple latex chunks to get it to work (see Unable to include multiple flextable in RMarkdown PDF minipage). However, since the minipages need to be included in a loop, I am trying to get the latex chunks to be inside R code that I can loop over. Unfortunately, I cannot get this to work.
This is a minimal reproducible example of what I can get working without a loop:
---
output:
pdf_document:
includes:
latex_engine: xelatex
documentclass: article
header-includes:
- \usepackage[a4paper]{geometry}
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
```{=latex}
\begin{minipage}[t]{0.5\linewidth}
```
```{r, Table1, warning = FALSE, message = FALSE, echo = FALSE}
library(flextable)
ft1 <- flextable(iris[1:20, 1:3])
ft1
```
```{=latex}
\end{minipage}%
\begin{minipage}[t]{0.5\linewidth}
```
```{r, Table2, warning = FALSE, message = FALSE, echo = FALSE}
ft2 <- flextable(iris[21:40, 1:3])
ft2
```
```{=latex}
\end{minipage}
```
To get the latex chunks inside an R chunk, I have tried several things, including using cat, asis_output (although this reportedly does not work inside loops anyway), pander and knitr::raw_latex (all of which I have tried both with either one and two backslashes before 'begin' and 'linewidth'), but none of it is working.
If there is another way to solve this problem by not having to put latex chunks inside an R chunk, I am also all ears.
You can use knitr::knit_child()
to get the string for the output and knitr::knit_expand()
for the dynamic content, along with the code chunk option results='asis'
to output content as raw Markdown.
Put the reusable RMarkdown content (LaTeX chunks, R chunks, etc.) in a string or file template, and delimit placeholder variables with {{...}}
in places where you want your dynamic content (e.g. flextable({{var1}})
).
Call knitr::knit_expand()
on the template and pass the strings to fill the template.
Call knitr::knit_child()
on the expanded string.
Call cat()
to output the result with the code chunk option results='asis'
.
Example (using a file template)
template.Rmd
```{=latex}
\begin{minipage}[t]{0.5\linewidth}
```
```{r, echo = FALSE}
flextable({{mydata[1]}})
```
```{=latex}
\end{minipage}%
\begin{minipage}[t]{0.5\linewidth}
```
```{r, echo = FALSE}
flextable({{mydata[2]}})
```
```{=latex}
\end{minipage}
```
main.Rmd
---
output:
pdf_document:
latex_engine: xelatex
---
```{r, warning=FALSE, echo=FALSE}
library(flextable)
library(knitr)
```
```{r, echo=FALSE, results='asis'}
d <- rep("iris[sample.int(nrow(iris), 4), 1:3]", 2)
src <- lapply(1:2, function(i) { knit_expand("template.Rmd", mydata=d) })
res <- knit_child(text = unlist(src), quiet = TRUE)
cat(res, sep = '\n')
```
Output
Note : if using a string template, to deal with special characters such as backslashes and backticks, you can :
Use a raw string (r"(...)"
) to prevent the LaTeX backslashes from being parsed as escape sequences;
Delimit the R code chunk containing the string with at least 4 backticks to account for the triple backticks in the string.
````{r, echo=FALSE}
template <- (r"(```{=latex}
\begin{minipage}[t]{0.5\linewidth}
```
<!-- ...more stuff... -->
```{=latex}
\end{minipage}
```)"
)
````
The other difference is that you have to use the text
argument when calling knit_expand()
:
```{r, echo=FALSE, results='asis'}
d <- c("iris[1:2, 1:3]", "iris[3:4, 1:3]")
cat(knit_child(text=knit_expand(text=template, mydata=d), quiet=TRUE))
```