By the end of this lecture, students should be able to:
knitr,
and Pandoc..Rmd
source.A common statistical workflow involves running code in one file, copying results into a word processor, and manually updating numbers, tables, and figures. This can lead to:
R Markdown combines narrative, executable R code, and the resulting output in one source document. Re-rendering the document runs the analysis again and updates the report.
An R Markdown file has the extension .Rmd. It can
contain:
The source file is a reproducible recipe for producing a report; the HTML or PDF file is the rendered report.
A reproducible report makes the analysis steps visible and rerunnable. A useful report should identify the data, document important decisions, show appropriate results, and state software or package requirements when relevant.
R Markdown helps, but it does not automatically guarantee reproducibility. For example, a report can still depend on a file stored on one person’s computer, an unrecorded package version, a changing online data source, or random numbers without a fixed seed.
Good practice: keep the source .Rmd,
data files, and any supporting scripts together in a clearly organised
project folder.
The traditional R Markdown workflow uses several tools:
.Rmd) stores prose,
metadata, and code chunks.A simplified view is:
.Rmd source
|
v
knitr executes R code and captures output
|
v
Intermediate Markdown
|
v
Pandoc converts the document
|
+------> HTML
|
+------> PDF (typically via LaTeX)
|
+------> Word
rmarkdown_lecture.Rmd.The Knit button commonly renders the default output format specified in YAML. To render another format, change the output configuration or use an appropriate render command.
Run this in the R Console, not inside every report:
For PDF output, install a LaTeX distribution. A convenient option for many R users is TinyTeX:
After installing TinyTeX, restart RStudio if necessary. Installing LaTeX may take several minutes and requires an internet connection. On managed university computers, follow local software installation policies.
YAML is the metadata block at the beginning of an R Markdown file. It
begins and ends with a line containing three hyphens (---).
Indentation matters; use spaces rather than tabs.
Consider this example:
---
title: "Statistical Analysis Report"
author: "Student Name"
date: "11 October 2026"
output:
html_document:
toc: true
number_sections: true
pdf_document:
toc: true
number_sections: true
---| Field | Purpose | Example |
|---|---|---|
title |
Report title | "Statistical Analysis" |
author |
Author or authors | "Student Name" |
date |
Date displayed in the report | "2026-10-11" |
output |
Output format and its settings | html_document: |
toc |
Adds a table of contents | true |
toc_depth |
Maximum heading depth in the contents | 3 |
number_sections |
Numbers headings | true |
code_folding |
Lets readers show or hide code in HTML | show |
theme |
Selects an HTML theme | readable |
highlight |
Syntax highlighting style | tango |
latex_engine |
Engine used for PDF typesetting | xelatex |
Options are format-specific. For example, code_folding
is an HTML option and does not control PDF code display. A valid option
for one output format may be ignored or cause an error under another
format.
The same source can define more than one output:
output:
html_document:
toc: true
toc_float: true
code_folding: show
pdf_document:
toc: true
number_sections: true
latex_engine: xelatexThis lets the author maintain one analytical source while producing different deliverables. The final appearance will not be identical because HTML and PDF have different layout systems and supported features.
Markdown is a lightweight way to format text.
The number of # symbols determines the heading level. In
a long report, use a logical heading hierarchy rather than skipping
levels unnecessarily.
Unordered list:
Ordered list:
Colons and hyphens in the separator row control alignment. Markdown tables are convenient for small, simple tables. For complex tables, use R packages designed for table formatting or LaTeX-specific tools when appropriate.
An R code chunk begins with three backticks and {r ...}
and ends with three backticks.
When rendered, R executes the code and inserts the results at that point in the report.
#> [1] 5.5
Expected output:
[1] 5.5
The exact display can vary slightly by output format, but the computed value should be 5.5.
Chunks can be given labels:
``` r
summary(x)
```
```
#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> 1.00 3.25 5.50 5.50 7.75 10.00
```Useful chunk labels:
Avoid spaces in chunk labels; use names such as
data-cleaning or model-fit.
Chunk options are written in the opening chunk header.
| Option | Meaning |
|---|---|
echo = TRUE |
Show the R code in the report |
echo = FALSE |
Hide the R code but keep its output |
eval = FALSE |
Show code without executing it |
include = FALSE |
Execute code but show neither code nor output |
results = 'hide' |
Hide printed text results |
message = FALSE |
Hide messages |
warning = FALSE |
Hide warnings |
fig.show = 'hide' |
Do not display generated figures |
error = TRUE |
Allow an error to be shown and continue knitting in supported cases |
collapse = TRUE |
Combine code and output in a more compact display |
These options have different purposes. In particular,
include = FALSE is not the same as
eval = FALSE.
#> [1] 15
Expected output:
[1] 15
The object lecture_constant is available to later chunks
because this chunk was executed.
The calculation runs, but the printed result is suppressed.
Teaching point: use echo = FALSE for
polished reports where the reader needs the result but not the code; use
include = FALSE for setup steps; use
eval = FALSE for code examples students should read but not
run automatically.
Instead of repeating the same options in every chunk, define them once in a setup chunk. This lecture uses:
knitr::opts_chunk$set(
echo = TRUE,
message = FALSE,
warning = FALSE,
fig.align = "center",
fig.width = 7,
fig.height = 4.5
)A chunk can override a global setting. For example:
```
#> mpg cyl disp hp
#> Min. :10.40 Min. :4.000 Min. : 71.1 Min. : 52.0
#> 1st Qu.:15.43 1st Qu.:4.000 1st Qu.:120.8 1st Qu.: 96.5
#> Median :19.20 Median :6.000 Median :196.3 Median :123.0
#> Mean :20.09 Mean :6.188 Mean :230.7 Mean :146.7
#> 3rd Qu.:22.80 3rd Qu.:8.000 3rd Qu.:326.0 3rd Qu.:180.0
#> Max. :33.90 Max. :8.000 Max. :472.0 Max. :335.0
#> drat wt qsec vs
#> Min. :2.760 Min. :1.513 Min. :14.50 Min. :0.0000
#> 1st Qu.:3.080 1st Qu.:2.581 1st Qu.:16.89 1st Qu.:0.0000
#> Median :3.695 Median :3.325 Median :17.71 Median :0.0000
#> Mean :3.597 Mean :3.217 Mean :17.85 Mean :0.4375
#> 3rd Qu.:3.920 3rd Qu.:3.610 3rd Qu.:18.90 3rd Qu.:1.0000
#> Max. :4.930 Max. :5.424 Max. :22.90 Max. :1.0000
#> am gear carb
#> Min. :0.0000 Min. :3.000 Min. :1.000
#> 1st Qu.:0.0000 1st Qu.:3.000 1st Qu.:2.000
#> Median :0.0000 Median :4.000 Median :2.000
#> Mean :0.4062 Mean :3.688 Mean :2.812
#> 3rd Qu.:1.0000 3rd Qu.:4.000 3rd Qu.:4.000
#> Max. :1.0000 Max. :5.000 Max. :8.000
```Global suppression of warnings is useful for presentation, but it should not be used to ignore problems during analysis. Investigate warnings while developing the report, then decide which messages are appropriate to show in the final document.
Inline R inserts a computed value directly into a sentence. The
syntax is a single backtick followed by r and an R
expression.
For example:
The resulting sentence displays the current value of the expression, not the code itself.
The sample contains 8 observations. Its mean is 79.38 and its standard deviation is 7.93.
Inline expressions are especially useful for sample sizes, estimates, confidence limits, p-values, and dates. They reduce manual transcription errors because the narrative updates when the data or analysis changes.
R Markdown supports LaTeX-style mathematics in HTML and PDF output.
Write $...$ around a short mathematical expression:
The sample mean is \(\bar{x} = \frac{1}{n}\sum_{i=1}^{n}x_i\).
Use $$...$$ on separate lines:
\[ s^2 = \frac{1}{n-1}\sum_{i=1}^{n}(x_i-\bar{x})^2 \]
Here, \(n\) is the sample size, \(\bar{x}\) is the sample mean, and \(s^2\) is the unbiased sample variance under the usual independent, identically distributed sampling framework with finite variance.
For a simple linear regression:
\[ Y_i = \beta_0 + \beta_1 x_i + \varepsilon_i, \qquad i=1,\ldots,n. \]
The model describes a response \(Y_i\) using a predictor \(x_i\), an intercept \(\beta_0\), a slope \(\beta_1\), and an error term \(\varepsilon_i\).
We will use a reproducible example of students’ study hours and assessment scores. These are simulated teaching data, not real student records.
set.seed(2026)
n <- 30
students <- data.frame(
student_id = sprintf("S%02d", 1:n),
study_hours = round(runif(n, min = 1, max = 10), 1)
)
students$score <- round(
pmin(100, pmax(0, 42 + 5.2 * students$study_hours +
rnorm(n, mean = 0, sd = 8))),
1
)
head(students, 6)The first six rows should display student identifiers, study hours, and scores. Since a fixed seed is used, the simulated values are reproducible in a consistent R environment.
#> study_hours score
#> Min. :1.000 Min. :44.30
#> 1st Qu.:2.325 1st Qu.:54.45
#> Median :4.000 Median :66.30
#> Mean :4.103 Mean :64.98
#> 3rd Qu.:5.975 3rd Qu.:73.67
#> Max. :8.700 Max. :93.30
This summary reports the minimum, quartiles, median, mean, and maximum for each numeric variable.
Compute selected statistics explicitly:
statistics <- data.frame(
Statistic = c("Sample size", "Mean score", "SD of score",
"Minimum score", "Maximum score"),
Value = c(
nrow(students),
mean(students$score),
sd(students$score),
min(students$score),
max(students$score)
)
)
statistics$Value <- round(statistics$Value, 2)
statisticsThis table is generated from the data, so it updates automatically if the data or calculations change.
plot(
students$study_hours,
students$score,
pch = 19,
col = "steelblue",
xlab = "Study hours",
ylab = "Assessment score",
main = "Study hours and assessment score"
)
abline(lm(score ~ study_hours, data = students),
lwd = 2, lty = 2, col = "darkorange")Simulated assessment score versus study hours.
The points represent simulated students, and the line is the fitted least-squares regression line. The plot illustrates association; it does not establish that study hours cause higher scores.
#>
#> Call:
#> lm(formula = score ~ study_hours, data = students)
#>
#> Residuals:
#> Min 1Q Median 3Q Max
#> -12.8707 -5.4702 0.0024 3.7286 13.6948
#>
#> Coefficients:
#> Estimate Std. Error t value Pr(>|t|)
#> (Intercept) 43.5641 2.8683 15.188 4.78e-15 ***
#> study_hours 5.2192 0.6143 8.496 3.09e-09 ***
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Residual standard error: 7.496 on 28 degrees of freedom
#> Multiple R-squared: 0.7205, Adjusted R-squared: 0.7105
#> F-statistic: 72.18 on 1 and 28 DF, p-value: 3.087e-09
Extract values for a concise narrative:
The estimated regression slope is 5.22 points per additional study hour, and the model \(R^2\) is 0.721. In this simulated example, the slope describes the estimated linear association in the generated sample. Statistical significance, uncertainty, model assumptions, and study design should also be considered before making substantive claims.
For a quick data preview, use:
knitr::kable(
head(students, 8),
caption = "First eight rows of the simulated student data",
digits = 1
)| student_id | study_hours | score |
|---|---|---|
| S01 | 7.3 | 90.7 |
| S02 | 6.0 | 78.1 |
| S03 | 2.3 | 55.7 |
| S04 | 3.6 | 54.3 |
| S05 | 6.0 | 78.7 |
| S06 | 1.2 | 45.6 |
| S07 | 5.2 | 67.7 |
| S08 | 8.7 | 76.1 |
knitr::kable() is useful for simple tables. More
advanced formatting may require packages such as kableExtra
or gt; their features and output compatibility should be
checked for both HTML and PDF.
knitr::kable(
head(students[c("student_id", "score")], 10),
col.names = c("Student", "Score"),
caption = "Student scores"
)| Student | Score |
|---|---|
| S01 | 90.7 |
| S02 | 78.1 |
| S03 | 55.7 |
| S04 | 54.3 |
| S05 | 78.7 |
| S06 | 45.6 |
| S07 | 67.7 |
| S08 | 76.1 |
| S09 | 70.9 |
| S10 | 74.6 |
For a report intended for public distribution, consider whether student identifiers or other data could disclose personal information. Use synthetic or properly de-identified data for demonstrations.
HTML is often a good choice for interactive or screen-based reports. Depending on the configuration, it can support:
HTML is usually easy to share, but a self-contained HTML file can become large when it embeds images and other resources.
PDF is useful for fixed-layout reports, printing, and formal submission. R Markdown’s standard PDF workflow typically uses LaTeX, so a LaTeX distribution is needed.
Common issues include:
The xelatex engine can be helpful for Unicode and font
handling, but it does not remove every font or package issue.
With the file saved as rmarkdown_lecture.Rmd, run:
# Render the default format(s) configured in YAML
rmarkdown::render("rmarkdown_lecture.Rmd")
# Render HTML only
rmarkdown::render(
"rmarkdown_lecture.Rmd",
output_format = "html_document"
)
# Render PDF only
rmarkdown::render(
"rmarkdown_lecture.Rmd",
output_format = "pdf_document"
)Run these commands in the R Console from the directory containing the file, or provide the correct file path. The output-format argument selects the format; options in YAML determine the corresponding settings.
| Feature | HTML | |
|---|---|---|
| Main rendering route | knitr and Pandoc | knitr, Pandoc, and typically LaTeX |
| Floating contents | Supported with suitable options | Not the same interactive behaviour |
| Code folding | Supported | Not supported in the same way |
| Page layout | Adapts to screen and browser | Fixed pages |
| Printing | Browser-dependent | Usually print-ready |
| Extra dependency | Browser to view | LaTeX distribution to build |
Choose HTML when readers will explore the report on screen or benefit
from navigation and code folding. Choose PDF when a stable page layout
is important. If both are required, maintain one well-structured
.Rmd file and test both formats rather than assuming that a
successful HTML render guarantees a successful PDF render.
Likely cause: a variable was not created, a chunk was not executed, or chunks appear in the wrong order.
Solution: ensure the object is defined in an earlier executed chunk. Restart R and render the entire document to check that it does not depend on objects left over in the interactive workspace.
Likely cause: the working directory differs from the folder containing the data file.
Solution: use an RStudio Project and a relative path
such as data/students.csv, or explicitly choose the correct
path. Avoid hard-coded paths that work only on one computer.
Likely cause: LaTeX is missing, a package is unavailable, or the source contains unsupported characters or layout.
Solution: inspect the first meaningful error in the render log, verify that LaTeX is installed, and test a minimal PDF document. If permitted, install TinyTeX and any missing packages.
Likely cause: two chunks use the same label.
Solution: give each labelled chunk a unique name,
such as summary-data, score-plot, or
regression-model.
Likely cause: the Console contains objects created manually in a previous session.
Solution: restart R and knit the complete document. A robust report should create the objects it needs rather than rely on hidden interactive state.
Likely cause: global options suppress warnings.
Solution: temporarily set
warning = TRUE and message = TRUE, inspect the
warnings, and address relevant issues. Suppressing warnings improves
appearance only when the analysis has already been checked.
Before submitting or distributing a report, check the following: