flowchart TB
A[🗂️ Raw Data] --> B[💻 Software <br> R / Python / Excel / SPSS]
B --> C[📊 Output <br> Tables & Figures]
C --> D[📋 Copy-Paste]
D --> E[📝 Word Document]
E --> F{Reviewer says: <br> 'Let's change X'}
F --> B
From Script to Document
One source file → HTML, PDF, Word, Slides, ePub…
April 29, 2026
github.com/Gelsleichter/quarto_ws
Yuri Gelsleichter
flowchart TB
A[🗂️ Raw Data] --> B[💻 Software <br> R / Python / Excel / SPSS]
B --> C[📊 Output <br> Tables & Figures]
C --> D[📋 Copy-Paste]
D --> E[📝 Word Document]
E --> F{Reviewer says: <br> 'Let's change X'}
F --> B
“2000 years later…” 📬
Reviewer 2:
“Please normalize variables before modeling.”
“Please add a Tukey post-hoc comparison.”
“Please remove the outlier in Block III and redo all analyses.”
You need to redo every table, every figure, every number in the text…
And your project folder looks like this:
📁 project/
├── data_raw.xlsx
├── data_clean.xlsx
├── data_clean_v2.xlsx
├── data_FINAL.xlsx
├── data_FINAL_really.xlsx
└── data_FINAL_USE_THIS_reviewed_v3.xlsx 🤯
For click button software (Excel, SPSS…):
Reproducibility is not just about code
It’s about recording decisions and methods — in any software.
Everything lived in one file?
Change the data → everything updates?
Automatically :)
Markup language = text with formatting instructions
| Year | Tool | What changed |
|---|---|---|
| 2004 | Markdown | Simple text formatting |
| 2012 | R Markdown | Markdown + R code chunks |
| 2022 | Quarto | Language-agnostic, multi-format, active development |
. . .
You can still render
.Rmdfiles with Quarto — nothing breaks.
| You type | You get |
|---|---|
# Header 1 |
Header level 1 |
## Header 2 |
Header level 2 |
**bold** |
bold |
*italic* |
italic |
~~strike~~ |
|
[text](url) |
clickable link |
 |
embedded image |
`code` |
inline code |
| You type | You get |
|---|---|
- item |
bullet list |
1. item |
numbered list |
> text |
blockquote |
--- |
horizontal rule |
^super^ |
superscript |
~sub~ |
subscript |
[^1] |
footnote reference |
This cover the Markdown rules to compose a Quarto document.
An open-source scientific and technical publishing system
Created by Posit (formerly RStudio)
The formula:
Code (data analysis) + Text (interpretation) + Output = One file
Supports R, Python, Julia, Observable
Reference: quarto.org
Also: websites, books, dashboards, manuscripts…
Same content, different formats — from a single .qmd file.
| IDE / Editor | Support |
|---|---|
| RStudio | Full (built-in) |
| VS Code | Full (extension) |
| Positron | Full (built-in) |
| Jupyter | Native |
| Terminal | CLI |
.qmd files are supported by the common code editor
Explore the Quarto Gallery:
This presentation is also a .qmd file!
Rendered with format: revealjs — the same system we’ll learn today.
Building a Quarto file, step by step
.qmd FileWe’ll construct a complete reproducible document using the Oats dataset:
The data:
The analysis:
cwt: “hundredweight” (centweight), 1 cwt = 112 lbs ~50.8 kg
Yield: 1/4 lbs per sub-plot (1/80 acre)
Original units from Yates (1935)
Every .qmd file starts with a YAML header between --- marks:
title, author, date → document metadataformat: html → output typeChange format: html to format: docx → the entire output changes to Word!
YAML (Yet Another Markup Language), is a human-friendly way to specify options and metadata
embed-resources: true
Produces a single, self-contained HTML file — all images, CSS, and JS bundled inside. Share via email, USB, or upload anywhere. No broken links!
The YAML header is the “brain” of your document — it controls the overall structure, format, and behavior.
More options in the Quarto formats and execution-options.
This study examines the effect of nitrogen on oat yield.
Key findings:
Note
Useful for supplementary information.
Tip
Share best practices and shortcuts.
Warning
Highlight potential issues or assumptions.
Important
Critical information — don’t skip this!
Code lives inside the document — it runs when you render.
You write:
| Option | What it controls | Default |
|---|---|---|
echo |
Show the code in the output? | true |
eval |
Run the code? | true |
include |
Include anything (code + output) in the document? | true |
output |
Show the results? | true |
By default, everything is shown and everything runs. You turn options off to control what the reader sees.
| You want… | Set this | Code | Runs | Output |
|---|---|---|---|---|
| Show everything (default) | (nothing) | ✅ | ✅ | ✅ |
| Hide code, show results | #| echo: false |
❌ | ✅ | ✅ |
| Show code, don’t run it | #| eval: false |
✅ | ❌ | ❌ |
| Run silently (setup) | #| include: false |
❌ | ✅ | ❌ |
For example: echo: true (default)
The reader sees both the code and the output:
Example: echo: false
The reader sees only the output — the code is hidden:
[1] 103.9722
eval: falseThe reader sees only the code — it doesn’t run:
Useful for showing examples or syntax without running them.
code-fold (click to toggle)When you set code-fold: true, the reader sees a button to expand:
Variety yield
1 Golden Rain 104.5000
2 Marvellous 109.7917
3 Victory 97.6250
Focus on results, but code still there. Same document.
knitr::kable| Variety | Mean Yield |
|---|---|
| Golden Rain | 104.5 |
| Marvellous | 109.8 |
| Victory | 97.6 |
Cross-reference: As shown in Table 1, Marvellous has the highest mean yield.
“The table number updates itself if modified.”
{gt} ✨Same data, publication-ready in a few extra lines:
Oats |>
dplyr::group_by(Variety, nitro) |>
dplyr::summarise(mean_yield = round(mean(yield), 1), .groups = "drop") |>
tidyr::pivot_wider(names_from = nitro, values_from = mean_yield,
names_prefix = "N = ") |>
gt::gt() |>
gt::tab_header(
title = "Oat Yield by Variety and Nitrogen",
subtitle = "Mean yield (bushels/acre)"
)| Oat Yield by Variety and Nitrogen | ||||
| Mean yield (bushels/acre) | ||||
| Variety | N = 0 | N = 0.2 | N = 0.4 | N = 0.6 |
|---|---|---|---|---|
| Golden Rain | 80.0 | 98.5 | 114.7 | 124.8 |
| Marvellous | 86.7 | 108.5 | 117.2 | 126.8 |
| Victory | 71.5 | 89.7 | 110.8 | 118.5 |
Reference figures and tables by their label:
Labels must start with a prefix
fig- for figures, tbl- for tables, eq- for equations, sec- for sections.
The dataset contains 72 observations.
The mean yield was 104 bushels/acre.
Yield ranged from 53 to 174.
The numbers come directly from the data. Change the data, the text updates.
ggplot2::ggplot(Oats, ggplot2::aes(x = nitro, y = yield, color = Variety)) +
ggplot2::geom_point(size = 2.5, alpha = 0.6) +
ggplot2::geom_smooth(method = "lm", se = TRUE, alpha = 0.2) +
ggplot2::labs(x = "Nitrogen (cwt/acre)", y = "Yield (bu/acre)") +
ggplot2::theme_minimal(base_size = 16) +
ggplot2::theme(legend.position = "top")Now we have Figure 1 (Figure 1) and Figure 2 (Figure 2). Add or remove figures — numbering is always correct.
Call:
lm(formula = yield ~ nitro + Variety, data = Oats)
Residuals:
Min 1Q Median 3Q Max
-33.725 -15.133 -1.392 12.333 54.275
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 82.400 5.483 15.029 < 2e-16 ***
nitro 73.667 11.192 6.582 7.96e-09 ***
VarietyMarvellous 5.292 6.130 0.863 0.391
VarietyVictory -6.875 6.130 -1.122 0.266
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 21.24 on 68 degrees of freedom
Multiple R-squared: 0.4102, Adjusted R-squared: 0.3841
F-statistic: 15.76 on 3 and 68 DF, p-value: 6.965e-08
The results flow into the document.
{broom} + {gt} ✨| Linear Model: Yield ~ Nitrogen + Variety | ||||||
| Oat yield experiment | ||||||
| term | estimate | std.error | statistic | p.value | conf.low | conf.high |
|---|---|---|---|---|---|---|
| (Intercept) | 82.400 | 5.483 | 15.029 | 0.000 | 71.459 | 93.341 |
| nitro | 73.667 | 11.192 | 6.582 | 0.000 | 51.334 | 96.000 |
| VarietyMarvellous | 5.292 | 6.130 | 0.863 | 0.391 | -6.941 | 17.524 |
| VarietyVictory | -6.875 | 6.130 | -1.122 | 0.266 | -19.107 | 5.357 |
From raw output to publication table — no manual formatting. See Table 3.
{equatiomatic} ✨Extract the symbolic equation directly from the model:
\[ \begin{aligned} \operatorname{yield} &= \alpha + \beta_{1}(\operatorname{nitro}) + \beta_{2}(\operatorname{Variety}_{\operatorname{Marvellous}}) + \beta_{3}(\operatorname{Variety}_{\operatorname{Victory}})\ + \\ &\quad \epsilon \end{aligned} \]
Now with estimated coefficients:
\[ \begin{aligned} \operatorname{\widehat{yield}} &= 82.4 + 73.67(\operatorname{nitro}) + 5.29(\operatorname{Variety}_{\operatorname{Marvellous}}) - 6.87(\operatorname{Variety}_{\operatorname{Victory}}) \end{aligned} \]
The equation writes itself from the model object. Change the model, the equation updates.
{report} ✨We fitted a linear model (estimated using OLS) to predict yield with nitro and
Variety (formula: yield ~ nitro + Variety). The model explains a statistically
significant and substantial proportion of variance (R2 = 0.41, F(3, 68) =
15.76, p < .001, adj. R2 = 0.38). The model's intercept, corresponding to nitro
= 0 and Variety = Golden Rain, is at 82.40 (95% CI [71.46, 93.34], t(68) =
15.03, p < .001). Within this model:
- The effect of nitro is statistically significant and positive (beta = 73.67,
95% CI [51.33, 96.00], t(68) = 6.58, p < .001; Std. beta = 0.61, 95% CI [0.43,
0.80])
- The effect of Variety [Marvellous] is statistically non-significant and
positive (beta = 5.29, 95% CI [-6.94, 17.52], t(68) = 0.86, p = 0.391; Std.
beta = 0.20, 95% CI [-0.26, 0.65])
- The effect of Variety [Victory] is statistically non-significant and negative
(beta = -6.87, 95% CI [-19.11, 5.36], t(68) = -1.12, p = 0.266; Std. beta =
-0.25, 95% CI [-0.71, 0.20])
Standardized parameters were obtained by fitting the model on a standardized
version of the dataset. 95% Confidence Intervals (CIs) and p-values were
computed using a Wald t-distribution approximation.
The package
reportis handy, and works with several model outputs.It generates a narrative summary of the model results.
flowchart LR A[📄 .qmd file] --> B[🌐 HTML] A --> C[📑 PDF] A --> D[📝 Word] A --> E[🎤 Slides] A --> F[📚 ePub]
When you render to HTML with multiple formats, Quarto automatically adds download links for PDF, Word, and ePub — readers can choose!
python: /home/ds/.cache/R/reticulate/uv/cache/archive-v0/2jhW8OVLRnKsG4octEWJA/bin/python
libpython: /home/ds/.cache/R/reticulate/uv/python/cpython-3.12.13-linux-x86_64-gnu/lib/libpython3.12.so
pythonhome: /home/ds/.cache/R/reticulate/uv/cache/archive-v0/2jhW8OVLRnKsG4octEWJA:/home/ds/.cache/R/reticulate/uv/cache/archive-v0/2jhW8OVLRnKsG4octEWJA
virtualenv: /home/ds/.cache/R/reticulate/uv/cache/archive-v0/2jhW8OVLRnKsG4octEWJA/bin/activate_this.py
version: 3.12.13 (main, Mar 10 2026, 18:17:25) [Clang 21.1.4 ]
numpy: /home/ds/.cache/R/reticulate/uv/cache/archive-v0/2jhW8OVLRnKsG4octEWJA/lib/python3.12/site-packages/numpy
numpy_version: 2.4.4
NOTE: Python version was forced by py_require()
Same analysis, three languages, one document.
Quarto supports Python natively. From RStudio, we use {reticulate}:
Same document, same workflow — R and Python side by side.
Readers switch between themes — built-in!
A single HTML file with everything inside. The “portable PDF” of the HTML world.
.R) from reporting (.qmd). Load saved results with readRDS()..qmd files (books, websites)..docx, share with co-authors, incorporate feedback — or use version control (Git).Tip
These aren’t reasons to avoid Quarto — they’re strategies for using it well.
Open RStudio → File → New File → Quarto Document
If you have RStudio (Quarto is built in) — follow along!
If not — All materials in the link/QR code.
github.com/Gelsleichter/quarto_ws
.qmd file (YAML + Markdown + Code)kable, gt), figures (ggplot2), inline code@fig-, @tbl-)equatiomatic), tidy tables (broom), auto-reports (report)reticulate)| Shortcut | Action |
|---|---|
Ctrl + Shift + K |
Render document |
Ctrl + Alt + I |
Insert code chunk |
Ctrl + Shift + Enter |
Run current chunk |
Ctrl + Enter |
Run current line |
Ctrl + Shift + M |
Insert pipe |> |
ggplot2 · gt · equatiomatic · broom · report · knitr · reticulate · qrcode · dplyr · tidyr
📧 Gelsleichter.Yuri.Andrei@uni-mate.hu
github.com/Gelsleichter/quarto_ws
Reproducible Research with Quarto | Yuri Gelsleichter