11.11 Output text as raw Markdown content (*)

By default, text output from code chunks will be written out verbatim with two leading hashes (see Section 11.12). The text is verbatim because knitr puts it in fenced code blocks. For example, the raw output of the code 1:5 from knitr is:

## [1] 1 2 3 4 5

Sometimes you may not want verbatim text output, but treat text output as Markdown content instead. For example, you may want to write out a section header with cat('# This is a header'), but the raw output is:

## # This is a header

You do not want the text to be in a fenced code block (or the leading hashes). That is, you want the raw output to be exactly the character string passed to cat():

# This is a header

The solution to this problem is the chunk option results = 'asis'. This option tells knitr not to wrap your text output in verbatim code blocks, but treat it “as is.” This can be particularly useful when you want to generate content dynamically from R code. For example, you may generate the list of column names of the iris data from the following code chunk with the option results = 'asis':

cat(paste0("- `", names(iris), "`"), sep = "\n")
  • Sepal.Length
  • Sepal.Width
  • Petal.Length
  • Petal.Width
  • Species

The hyphen (-) is the syntax for unordered lists in Markdown. The backticks are optional. You can see the verbatim output of the above chunk without the results = 'asis' option:

cat(paste0("- `", names(iris), "`"), sep = "\n")
- `Sepal.Length`
- `Sepal.Width`
- `Petal.Length`
- `Petal.Width`
- `Species`

Below is a full example that shows how you can generate section headers, paragraphs, and plots in a for-loop for all columns of the mtcars data:

title: Generate content programmatically

With the chunk option `results = 'asis'`, you can
write out text as raw Markdown content, which can
also be mixed with plots.

```{r, mtcars-plots, results='asis'}
for (i in names(mtcars)) {
  cat('\n\n# Summary of the variable `', i, '`\n\n')
  x <- mtcars[, i]
  if (length(unique(x)) <= 6) {
    cat('`', i, '` is a categorical variable.\n\n')
    plot(table(x), xlab = i, ylab = 'Frequency', lwd = 10)
  } else {
    cat('Histogram for the continuous variable `', i, '`.\n\n')
    hist(x, xlab = i, main = '')

Please note that we added line breaks (\n) excessively in the code. That is because we want different elements to be separated clearly in the Markdown content. It is harmless to use an excessive number of line breaks between different elements, whereas it can be problematic if there are not enough line breaks. For example, there is much ambiguity in the Markdown text below:

# Is this a header?
Is this a paragraph or a part of the header?
![How about this image?](foo.png)
# How about this line?

With more empty lines (which could be generated by cat('\n')), the ambiguity will be gone:

# Yes, a header!

And definitely a paragraph.

![An image here.](foo.png)

# Absolutely another header

The cat() function is not the only function that can generate text output. Another commonly used function is print(). Please note that print() is often implicitly called to print objects, which is why you see output after typing out an object or value in the R console. For example, when you type 1:5 in the R console and hit the Enter key, you see the output because R actually called print(1:5) implicitly. This can be highly confusing when you fail to generate output inside an expression (such as a for-loop) with objects or values that would otherwise be correctly printed if they were typed in the R console. This topic is quite technical, and I have written the blog post “The Ghost Printer behind Top-level R Expressions” to explain it. If you are not interested in the technical details, just remember this rule: if you do not see output from a for-loop, you should probably print objects explicitly with the print() function.