04 · R Markdown Reports¶
Every module so far has run R code and shown you the output separately.
R Markdown (.Rmd) files combine narrative text, R code, and rendered
output into a single document — the code chunks actually run when the
document is rendered, and the results (tables, printed values, plots) are
woven directly into the final HTML, PDF, or Word file. This is how you
turn a one-off analysis script into a shareable, reproducible report.
Anatomy of an .Rmd file¶
---
title: "Sales Summary"
output: html_document
params:
min_qty: 2
---
## Overview
This report summarizes orders with quantity at least `r params$min_qty`.
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
library(dplyr)
```
```{r}
orders <- tibble(order_id = 1:5, qty = c(1, 3, 2, 5, 1))
orders %>% filter(qty >= params$min_qty)
```
Three distinct pieces make up the file: the YAML header (between the
--- fences) sets the title, output format, and any params the report
accepts; narrative text is plain Markdown, with inline R via
`r expression` for values that should appear in a sentence; code
chunks (fenced with ```{r}) are R code that actually executes when
the document renders, with its printed output inserted into the document
right where the chunk sits.
Rendering¶
render() runs every code chunk top to bottom in a fresh R session,
knits the results into Markdown, then hands that off to Pandoc to produce
the final HTML (or PDF, or Word doc, depending on the output: field).
The params list lets one .Rmd template drive multiple reports with
different inputs — the report above with min_qty = 2 filters
differently than the same file rendered with min_qty = 4, with no code
changes needed.
Chunk options that matter¶
| Option | Effect |
|---|---|
echo = FALSE |
Run the code, but don't show the code itself — only its output |
echo = TRUE (default) |
Show both the code and its output |
include = FALSE |
Run the code, show nothing (used for setup/library-loading chunks) |
eval = FALSE |
Show the code, don't run it (for illustrating syntax without executing) |
message = FALSE / warning = FALSE |
Suppress package-load messages or warnings from cluttering the report |
fig.width, fig.height |
Control the size of any plot the chunk produces |
A report meant for a non-technical audience typically sets echo = FALSE
globally in the setup chunk (knitr::opts_chunk$set(echo = FALSE)) so
the reader sees findings, not source code; a report meant to teach or
document the method itself does the opposite.
Parameterized reports for repeat use¶
The real payoff of params shows up when the same report needs to run
for several inputs — one region, one month, one customer segment — without
copy-pasting the .Rmd file:
regions <- c("East", "West", "South")
for (r in regions) {
rmarkdown::render(
"report.Rmd",
params = list(region = r),
output_file = paste0("report_", r, ".html")
)
}
Trap: render() runs in a fresh environment by default, which means
any object created outside the .Rmd (in your interactive session) is
invisible inside it — a report that works when you manually step through
its chunks in the console can fail with "object not found" the moment you
call render(), because the console had leftover variables the fresh
render session does not. Every object the report needs must be created
inside the .Rmd itself, typically in the setup chunk.
Tables that render properly¶
A bare print() of a data frame inside a chunk produces plain
fixed-width text, which looks fine in the R console but often wraps badly
in HTML output. knitr::kable() produces a properly formatted table in
whatever output format you're rendering to:
Cheat sheet¶
| Task | How |
|---|---|
| Render a report | rmarkdown::render("file.Rmd") |
| Pass different inputs to the same template | params: in YAML + params = list(...) at render time |
| Show output but hide the code | chunk option echo = FALSE |
| Run code with no output at all shown | chunk option include = FALSE |
| Show code without running it | chunk option eval = FALSE |
| Render a well-formatted table | knitr::kable(df) |
| Inline a computed value in a sentence | `r expression` |
How It Actually Works¶
Knitting an .Rmd/.qmd file is a two-stage pipeline, not one program:
first knitr parses the document, finds each fenced code chunk, and
evaluates it in a persistent R session (chunk options like echo/
eval/cache control exactly how each chunk's code and output get woven
back into the surrounding text) — producing a plain Markdown file with
code/output already substituted in. Second, Pandoc (a separate
document-conversion binary R Markdown bundles and calls out to) takes that
intermediate Markdown and converts it into HTML, PDF (via a LaTeX engine),
or Word, using its own internal document AST and format-specific writers.
cache = TRUE on a chunk works by hashing the chunk's code text plus its
declared dependencies; if that hash matches a previous run's cached hash,
knitr loads the previously-saved result objects from disk instead of
re-executing the code — which is why changing so much as a comment inside
a cached chunk forces a re-run (the hash changes) while an unrelated
earlier chunk's edit doesn't invalidate a later chunk's cache unless you've
declared a dependency between them.
Exercise¶
- Write an
.Rmdfile with aparamsblock for amin_qtythreshold, render it twice with two different values viarmarkdown::render(), and confirm the two output files show different filtered tables. - Add a chunk that produces a
ggplot2plot, and usefig.width/fig.heightchunk options to control its rendered size. - Convert a table in one of your reports from a bare
print()toknitr::kable()and compare the two renders — note whenkable()'s cleaner formatting actually matters (long reports, non-technical audiences) versus when it's overkill (a quick internal script).