0.1 Learning outcomes

By the end of this lecture, students should be able to:

  1. Explain what R Markdown is and why it supports reproducible research.
  2. Describe the roles of Markdown, R code chunks, knitr, and Pandoc.
  3. Construct and interpret a YAML header.
  4. Write Markdown text, inline R expressions, equations, tables, and code chunks.
  5. Control whether code, output, messages, warnings, and figures appear in a report.
  6. Generate HTML and PDF reports from the same .Rmd source.
  7. Diagnose common rendering problems and apply a reproducible workflow.

0.2 Suggested two-hour teaching plan

1 1. Why R Markdown?

1.1 1.1 The problem with separate analysis and reporting

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:

  • Results in the report that no longer match the current analysis.
  • Manual copying errors.
  • Difficulty reproducing the analysis months later.
  • Inconsistent figures, labels, or numerical precision.
  • A report that does not explain exactly how the results were obtained.

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.

1.2 1.2 What is R Markdown?

An R Markdown file has the extension .Rmd. It can contain:

  • YAML metadata at the top, specifying the title, author, date, and output settings.
  • Markdown text for headings, paragraphs, lists, emphasis, and links.
  • R code chunks that execute R code.
  • Inline R expressions that insert computed values into prose.
  • Mathematics written with LaTeX-style notation.
  • Tables, plots, and other output generated during rendering.

The source file is a reproducible recipe for producing a report; the HTML or PDF file is the rendered report.

1.3 1.3 R Markdown and reproducibility

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.

2 2. The rendering workflow

2.1 2.1 The main components

The traditional R Markdown workflow uses several tools:

  1. R Markdown (.Rmd) stores prose, metadata, and code chunks.
  2. knitr executes R chunks and collects their results.
  3. Pandoc converts the knitted document into a target format such as HTML, PDF, or Word.
  4. LaTeX is typically used to produce PDF output from R Markdown’s PDF workflow.

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

2.2 2.2 Create and render a document in RStudio

  1. Open RStudio.
  2. Select File → New File → R Markdown.
  3. Enter a title and author, then choose an output format.
  4. Save the file with an informative name, such as rmarkdown_lecture.Rmd.
  5. Click Knit to render the document.

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.

2.3 2.3 Install the required packages

Run this in the R Console, not inside every report:

install.packages(c("rmarkdown", "knitr"))

For PDF output, install a LaTeX distribution. A convenient option for many R users is TinyTeX:

install.packages("tinytex")
tinytex::install_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.

3 3. Understanding the YAML header

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
---

3.1 3.1 Common YAML fields

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.

3.2 3.2 Multiple output formats

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: xelatex

This 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.

3.3 3.3 Inline R in YAML

This date field is evaluated during rendering:

date: "11 October 2026"

It avoids manually updating the date every time the report is rendered. If a fixed submission date is required, use a literal date instead.

4 4. Markdown essentials

Markdown is a lightweight way to format text.

4.1 4.1 Headings and emphasis

# Main section
## Subsection
### Smaller subsection

**Bold text**
*Italic text*
`inline code`

The number of # symbols determines the heading level. In a long report, use a logical heading hierarchy rather than skipping levels unnecessarily.

4.2 4.2 Lists

Unordered list:

- First item
- Second item
  - Nested item

Ordered list:

1. Define the research question.
2. Inspect the data.
3. Fit the model.
4. Interpret the results.

4.4 4.4 Tables in Markdown

| Variable | Mean | Standard deviation |
|:--|--:|--:|
| Score | 75.4 | 8.2 |
| Age | 24.1 | 3.7 |

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.

5 5. R code chunks

5.1 5.1 Basic chunk structure

An R code chunk begins with three backticks and {r ...} and ends with three backticks.


``` r
x <- 1:10
mean(x)
```

```
#> [1] 5.5
```

When rendered, R executes the code and inserts the results at that point in the report.

5.2 5.2 A first executable example

x <- 1:10
mean_x <- mean(x)
mean_x
#> [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.

5.3 5.3 Naming chunks

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:

  • Help identify where a result comes from.
  • Make errors easier to locate.
  • Support navigation in a long source file.
  • Should be unique within a document.

Avoid spaces in chunk labels; use names such as data-cleaning or model-fit.

5.4 5.4 Important chunk options

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.

5.5 5.5 Demonstrating the difference between options

5.5.1 Show code and output

sum(1:5)
#> [1] 15

5.5.2 Hide code but show output

#> [1] 15

Expected output:

[1] 15

5.5.3 Show code but do not execute it

# This code is displayed but not evaluated.
sqrt(81)

5.5.4 Execute setup code without displaying it

The object lecture_constant is available to later chunks because this chunk was executed.

5.5.5 Hide printed output

summary(c(2, 4, 4, 6, 9))

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.

6 6. Global chunk options

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.

7 7. Inline R expressions

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 sample contains 10 observations.
The mean is 5.5.

The resulting sentence displays the current value of the expression, not the code itself.

exam_scores <- c(68, 74, 81, 77, 90, 85, 72, 88)

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.

8 8. Mathematical notation

R Markdown supports LaTeX-style mathematics in HTML and PDF output.

8.1 8.1 Inline mathematics

Write $...$ around a short mathematical expression:

The sample mean is $\bar{x} = \frac{1}{n}\sum_{i=1}^{n}x_i$.

The sample mean is \(\bar{x} = \frac{1}{n}\sum_{i=1}^{n}x_i\).

8.2 8.2 Display mathematics

Use $$...$$ on separate lines:

$$
s^2 = \frac{1}{n-1}\sum_{i=1}^{n}(x_i-\bar{x})^2
$$

\[ 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.

8.3 8.3 A statistical model

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\).

9 9. Data, tables, and plots

9.1 9.1 Create a small data set

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.

9.2 9.2 Summarise the data

summary(students[c("study_hours", "score")])
#>   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)
statistics

This table is generated from the data, so it updates automatically if the data or calculations change.

9.3 9.3 Make a plot

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.

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.

9.4 9.4 A regression model

fit <- lm(score ~ study_hours, data = students)
summary(fit)
#> 
#> 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:

intercept <- unname(coef(fit)[1])
slope <- unname(coef(fit)[2])
r_squared <- summary(fit)$r.squared

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.

10 10. Tables and presentation

10.1 10.1 A simple Markdown table

| Measure | Description |
|:--|:--|
| Mean | Arithmetic average |
| Median | Middle ordered value |
| Standard deviation | Measure of spread |

10.2 10.2 A table generated from R

For a quick data preview, use:

knitr::kable(
  head(students, 8),
  caption = "First eight rows of the simulated student data",
  digits = 1
)
First eight rows of the simulated student data
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.

10.3 10.3 Display only selected columns

knitr::kable(
  head(students[c("student_id", "score")], 10),
  col.names = c("Student", "Score"),
  caption = "Student scores"
)
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.

11 11. HTML and PDF: what changes?

11.1 11.1 HTML output

HTML is often a good choice for interactive or screen-based reports. Depending on the configuration, it can support:

  • A floating table of contents.
  • Code folding.
  • Paged data-frame output.
  • Web-friendly navigation and hyperlinks.
  • Styling through HTML themes and CSS.

HTML is usually easy to share, but a self-contained HTML file can become large when it embeds images and other resources.

11.2 11.2 PDF output

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:

  • LaTeX not installed or not found by R.
  • Missing LaTeX packages.
  • Long tables or wide figures extending beyond the page.
  • Font or Unicode problems.
  • HTML-only features not being available in PDF.

The xelatex engine can be helpful for Unicode and font handling, but it does not remove every font or package issue.

11.3 11.3 Render formats from R

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.

11.4 11.4 A compact comparison

Feature HTML PDF
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

11.5 11.5 Choosing an output format

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.

12 12. Common errors and troubleshooting

12.1 12.1 “object not found”

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.

12.2 12.2 File not found

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.

12.3 12.3 PDF rendering fails

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.

12.4 12.4 Duplicate chunk labels

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.

12.5 12.5 Code runs in the Console but knitting fails

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.

12.6 12.6 Warnings disappear

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.

13 13. Reproducibility checklist

Before submitting or distributing a report, check the following: