Skip to course content
Free R statistics course

Statistical Data Analytics with R

Unit 01.01: Scripts compute, reports explain

Scripts compute. Reports explain. When one file tries to do both, the numbers and the sentences drift apart.

Two files, two jobs

A script's job is to produce results and save them. It has no reader; it has an output. A report's job is to explain results to somebody, and it should not be computing anything of consequence while it does so.

Splitting them has a practical payoff. You can re-run a slow analysis without re-rendering the report, and re-render the report — after a wording change — without re-running the analysis. More importantly, the report can only cite numbers the script actually saved, so it cannot contain a figure that no code produced.

The report still gets its numbers from code, through inline expressions. What it does not do is recompute the model in a hidden chunk.

This block writes both files so the division of labour is visible.

# Scripts compute. Reports explain. Keep the two jobs in different files.
project <- file.path(tempdir(), "split-demo")
dir.create(file.path(project, "R"), recursive = TRUE, showWarnings = FALSE)

script <- c(
  "# R/01-analysis.R -- computes, saves, returns nothing to a reader",
  "sessions <- data.frame(variant = rep(c('A','B'), each = 200),",
  "                       abandoned = c(rep(TRUE, 84), rep(FALSE, 116),",
  "                                     rep(TRUE, 68), rep(FALSE, 132)))",
  "result <- prop.test(table(sessions$variant, sessions$abandoned))",
  "saveRDS(result, 'outputs/test.rds')"
)
report <- c(
  "<!-- report.qmd -- explains, cites the saved result, computes nothing -->",
  "The abandonment rate fell by `r round(diff, 1)` percentage points",
  "(95% CI `r ci[1]` to `r ci[2]`)."
)
writeLines(script, file.path(project, "R", "01-analysis.R"))
writeLines(report, file.path(project, "report.qmd"))

cat("Script lines:", length(script), " Report lines:", length(report), "\n")
cat("Numbers typed into the report by hand:", 0, "\n\n")
cat("The script can be re-run without re-rendering the report.\n")
cat("The report cannot produce a number the script did not save.\n")

The script is six lines that end in a saved result object. The report is three lines that cite it. Numbers typed into the report by hand: 0. Every figure in the prose is an inline expression pointing at something the script computed, which is the property that makes the two files stay in agreement.

The mistake this prevents

The mistake is one enormous Quarto file that loads the data, fits the models and writes the discussion. Rendering takes minutes, so you stop rendering, and the published document slowly stops matching the code.

Takeaway

Keep computation in scripts and explanation in the report. Save results as objects, and let the report cite them through inline expressions rather than recomputing.