48

I am using knitr to parse an R Markdown document . Is there a way to conditionally display a block of text in R Markdown depending on a variable in the environment I pass into knitr?

For instance, something like:

`r if(show.text) {`
  la la la
`r }`

Would print "la la la" in the resulting doc if show.text is true.

andrew
  • 2,524
  • 2
  • 24
  • 36
  • 1
    Unfortunately I am not sure about `knitr`, but you can do that easily with `pander` and the `<% ... %>` type of tags: http://rapporter.github.io/pander/#brew-to-pandoc – daroczig Aug 20 '14 at 14:29

8 Answers8

76

You need a complete R expression, so you cannot break it into multiple blocks like you show, but if the results of a block are a text string then it will be included as is (without quotes), so you should be able to do something like:

`r if(show.text){"la la la"}`

and it will include the text if and only if show.text is TRUE.

Greg Snow
  • 48,497
  • 6
  • 83
  • 110
  • 14
    This is fantastic and should be accepted. Even extends to more complicated markdown input e.g. `\`r if(TRUE){"#Header\nIs this under the header\n\n##subheader\nis this undersub?"}\`` – James Owers Dec 12 '15 at 02:28
  • I particularly like this solution, because in some cases, it's tough to get the line breaks to behave. Sometimes, it's just easier to plop in your conditional text in the main body of the document rather than in a code chunk with `results = "asis"` so you don't have to mess with endless tries of `\n`, `
    `, `\newline`, etc.
    – Nova Aug 06 '21 at 14:47
40

You can do this using the "eval" chunk option. See http://yihui.name/knitr/options/.

```{r setup, echo=FALSE}
show_text <- FALSE
````

```{r conditional_block, eval=show_text}
print("this will only print when show.text is TRUE")
```

I've been using YAML config files to parameterize my markdown reports which makes them more reusable.

```{r load_config}
library(yaml)
config <- yaml.load_file("config.yaml")
```

...

```{r conditional_print, eval=config$show_text}
print("la la la")
````
CL.
  • 14,577
  • 5
  • 46
  • 73
paul-boardman
  • 499
  • 4
  • 7
  • 4
    Note that as opposed to [Greg Snow's answer](http://stackoverflow.com/a/25407811/2706569), this solution prints the text with "output markup" and does not display it verbatim. – CL. Dec 07 '16 at 15:46
  • 2
    It is because the `results = 'asis'` is missing. The `asis` knitr engine is better suited for this task: https://stackoverflow.com/a/66454300/3436535 – cderv Mar 03 '21 at 09:40
37

I find it easiest to do this by putting all of my text into a separate file and then include it from the main file with:

```{r conditional_print, child='text.Rmd', eval = show_text}
```

This has the advantage that you can still put inline R statements or other chunks into the child file, so that if you change your mind about what counts as optional text, you don't have to refactor your project.

jkeirstead
  • 2,881
  • 3
  • 23
  • 26
28

Here's a tweak to Paul Boardman's approach that gives proper markup in the output.

```{r setup, echo=FALSE}
show_text <- FALSE
```

```{r conditional_block, echo=FALSE, results='asis', eval=show_text}
cat("## Hey look, a heading!

lorem ipsum dolor emet...")
```

Even better, if we invoke the python engine to generate our output, we can use triple quoting to easily handle text that contains single or double quotes without needing to do anything fancy:

```{python, conditional_block_py, echo=FALSE, results='asis', eval=show_cond_text}
print("""
## Still a heading

Block of text with 'single quotes' and "double quotes"
""")
```
David Marx
  • 8,172
  • 3
  • 45
  • 66
  • I appreciate the Python solution but it makes it difficult if you need to use R objects in that code block as well. – ctbrown Nov 15 '17 at 02:05
  • @ctbrown if you need to run R code from inside your markdown, e.g. `cat("This year, we sold \`r quarterlySales\` units of product")` then neither the python solution nor the R solution I presented here will work for you. Instead of trying to call R code directly in the markdown, you could just build the text you need by pasting stuff piecewise, e.g. `cat(paste("This year, we sold", quarterlySales, "units of product"))`. If this isn't what you meant, feel free to elaborate on your use case and I'll see what I can come up with. – David Marx Nov 16 '17 at 23:41
  • This is great, perfect for what I need. The conditional block only seems to work when knitting the output only though. It won't let me highlight a line of code from within the chunk and execute it, reporting an error and aborting the R session everytime. ```{r conditional_block, echo=FALSE, results='asis', eval=show_text} cat("## Hey look, a heading! lorem ipsum dolor emet...") ``` ```Error in class(obj) <- "rs.scalar" : cannot set attribute on a symbol``` In just a normal R chunk it works ```{r} cat("## Hey look, a heading! lorem ipsum dolor emet...") ``` Any ideas why? – Roasty247 Jun 29 '19 at 08:15
  • 1
    The knitr `asis` engine is a made for this https://bookdown.org/yihui/rmarkdown-cookbook/eng-asis.html It avoids to use `cat` and having quoting issue. – cderv Mar 03 '21 at 09:37
6

The solutions above may be a little clunky for larger blocks of text and not great for certain situations. Let's say I want to create a worksheet for students with some questions and also use the same .Rmd file to generate a file with solutions. I used basic LaTeX flow control:

``` {r, include = F}
# this can be e.g., in a parent .Rmd and the below can be in child
solution <- TRUE 
```
\newif\ifsol
\sol`r ifelse(solution, 'true', 'false')`

Then I can do:

What is $2 + 2$

\ifsol
4
\fi

This way you can also create alternative blocks of text using

\ifsol
Alternative 1
\else
Alternative 2
\fi
Milan Valášek
  • 571
  • 3
  • 10
  • Hi Milan: I tried to replicate your idea and it didn't work (both `Alternative`s print out). I'd love to be able to make it work! My code is all under one Rmd (no children). Any thoughts? I'd appreciate any help. – Marian Minar Jan 23 '19 at 16:32
  • Hi @MarianMinar, I literally just copied the code above into a new .Rmd and it works just fine. Make sure you create the `solution` object in an R code chunk as it gets used to set \sol to true or false. – Milan Valášek Jan 23 '19 at 16:53
  • Hi @Milan. Indeed, PDF and HTML outputs are good. I found out what the issue is: my `output` (in YAML) is `ioslides_presentation` does not allow for this alternate LaTeX output. Would you have any idea why? – Marian Minar Jan 23 '19 at 22:38
  • Nope, no idea. I would be interested to learn the reason if you find out though! Does any LaTeX work with isoslides? To be honest, I'm slightly surprised it works for HTML... – Milan Valášek Jan 24 '19 at 10:06
  • Yes, LaTeX renders! The magic of pandoc, knitr, etc. I'm currently exploring a few `YAML` arguments and I'll post to this comment thread if I find anything interesting. – Marian Minar Jan 24 '19 at 16:36
  • 1
    @MilanValášek: For me, it does not work in HTML, only in LaTex. If you find a solution as beautiful and simple as yours for HTML, I'd be highly interested! – mavericks Sep 11 '19 at 15:39
  • 1
    @mavericks Yes, I just started to grapple with the same problem. If anyone can help, that would be great! – Milan Valášek Sep 12 '19 at 15:53
  • 1
    @mavericks I ended up writing a function that does a lot of post-processing not too unlike what Tommi Härkänen did below. However, a simple, if not terribly elegant, solution is to replace the LaTeX ifelse with `R` if-else statements that insert (or not) HTML comment markers "" around your alternative blocks of text. The commented out text will still be in the HTML file but will not be visible. You can even write yourself a function that makes it a little more efficient... – Milan Valášek May 14 '20 at 12:05
  • @MilanValášek: many thanks for the advice, will give it a try! – mavericks May 17 '20 at 09:08
  • You can use the `asis` engine for this I believe https://bookdown.org/yihui/rmarkdown-cookbook/eng-asis.html See https://stackoverflow.com/a/66454300/3436535 – cderv Mar 03 '21 at 09:39
6

The accepted answer above works well for inline content.

For block content (one of several paragraphs of Markdown text), knitr contains a asis engine that allow to include Markdown content conditionnally depending if chunk option echo = FALSE or echo = TRUE

Example from the doc https://bookdown.org/yihui/rmarkdown-cookbook/eng-asis.html

```{r}
getRandomNumber <- function() {
  sample(1:6, 1)
}
```

```{asis, echo = getRandomNumber() == 4}
According to https://xkcd.com/221/, we just generated
a **true** random number!
```

If you need to include more complexe content , like piece of a R Markdown document with e.g code block, then child document could be useful. See https://bookdown.org/yihui/rmarkdown-cookbook/child-document.html

cderv
  • 6,272
  • 1
  • 21
  • 31
3

Another way to add markdown text conditionally is below. It uses the block "engine" which seems to run fine although I'm not sure how to make inline R evaluation working.

Note that I use the view_all switch defined in the YAML metadata to control whether or not the block is visible.

Also, note that both eval and include chunk options are needed. The first prevents errors during regular Run All in RStudio. The second prevents output with Knit.

---
title: "Conditional Output"
params:
  view_all: false
output:
  html_document: default
  pdf_document: default
---

```{block eval=FALSE, include=params$view_all}
# Some simple markdown.

Some things work: $2 + 2^2 = 3\cdot2$

Other does not: 2 + 2 = `r 2+2`
```
Jacek
  • 1,048
  • 15
  • 21
  • 1
    Can these `include=` parameters be selected somehow in a reactive context for Shiny rmarkdown documents? – wdkrnls Oct 17 '19 at 20:52
  • 1
    `block` engine should be used for this purpose. It could break in the future as the intended engine for this is `asis` engine. See my answer https://stackoverflow.com/a/66454300/3436535 – cderv Mar 03 '21 at 09:35
1

I tried to define a function my.render(), which preprocesses the Rmd file, and depending on the commentout argument, either keeps the HTML commenting code (TRUE) in the Rmd file or removes them (FALSE). Then writes the preprocessed Rmd file into tmp.Rmd, and uses the usual render() function.

my.render <- function(input, commentout=FALSE, ...) {
  if (commentout == FALSE) {
    ## Delete the HTML comment lines from code
    txt <- readLines(input)
    txt[grepl(" *<!-- *| *--> *", txt)] <- ""
    write.table(txt, file="tmp.Rmd", sep="\n", quote=FALSE, row.names=FALSE, col.names=FALSE)
    render("tmp.Rmd", output_file=sub("Rmd","html",input), ...)
  } else {
    render(input, output_file=sub("Rmd","html",input), ...)
  }
}

It seemed to work. E.g.

<!--
This text with formulas $\alpha+\beta$ is visible, when commentout=FALSE.
-->