| Year | GDP per capita (US$) | Life expectancy |
|---|---|---|
| 2019 | 9,030 | 75.8 |
| 2020 | 7,074 | 74.5 |
| 2021 | 7,972 | 73.0 |
| 2022 | 9,281 | 74.9 |
| 2023 | 10,378 | 75.8 |
Lecture 11 - Quarto in Practice
quarto render builds a document; quarto preview keeps a live one openLast class you rendered one HTML page. Today that same skill fans out:
.bib filefreeze: rendering documents without re-running the world| You type | You get |
|---|---|
**bold**, *italic* |
bold, italic |
~~scratch that~~ |
|
[text](url) |
a link |
 |
an image |
`code` |
code |
> quote |
a blockquote |
2^10^, H~2~O |
210, H2O |
footnote[^1] |
a numbered footnote |
$\mu = \frac{1}{n}\sum x_i$ inline, $$ ... $$ for display equations# Heading 1
This is a paragraph[^1].
## Heading 2
This is *italic*, this is `code`,
this is ~~strikethrough~~.
This is a [link](https://www.emory.edu).
Equation: $\mu = \frac{1}{n} \sum_{i=1}^{n} x_i$
List:
- Item 1
- Item 2
- Subitem 1
[^1]: This is a footnote.
| Header 1 | Header 2 | Header 3 |
|:---------|:--------:|---------:|
| Cell 1 | Cell 2 | Cell 3 |Heading 1
This is a paragraph1.
Heading 2
This is italic, this is code, this is strikethrough.
This is a link. Equation: \(\mu = \frac{1}{n} \sum_{i=1}^{n} x_i\)
List:
| Header 1 | Header 2 | Header 3 |
|---|---|---|
| Cell 1 | Cell 2 | Cell 3 |
.ipynb notebooks directly. No rewriting needed--execute to re-run the code firstquarto check jupyter diagnoses it, and the QUARTO_PYTHON variable fixes it.qmd from the start is still nicer for long documents, but old notebooks convert as they areThe same notebook, rendered. More details here
quarto install tinytex)--execute re-runs it first:.bib file is plain text: one entry per source, each with a citation key (here, nash1950equilibrium)@nash1950equilibrium → Nash (1950)[@nash1950equilibrium] → (Nash 1950)[@nash1950equilibrium, p. 48] → (Nash 1950, p. 48)csl: apa.csl (thousands of styles here).bib file.bib files that stay in sync with your library@A citation points outside your document. A cross-reference points inside it. Label the chunk, then use the label:
That last line renders as Rainfall peaks in March (Figure 1)
Markdown tables take the label underneath, after a colon:
fig-, tbl-, eq-, sec-, lst-label: rainfall with no prefix and the caption still appears, with no number, while @rainfall sits in your text as raw text@sec- also needs number-sections: true in the YAML[-@fig-rain] prints the bare number, for when you are already inside bracketsThis is why we number nothing by hand. Hand-typed “Figure 3” is wrong the moment you add Figure 2
practice.qmd in VS Codetitle, author, format: html, and bibliography: references.bib## heading, a paragraph, a bulleted list, and a small Markdown tableplt.plot) with these chunk options:
#| echo: true and #| eval: true#| fig-cap: "Your caption here"#| label: fig-myplot@fig-myplotreferences.bib file with one BibTeX entry (grab one from Google Scholar)@keyThe chunk options are on last week’s table: fig- labels are what @fig- references point at
pandas shipped a new version and one default changedRendering and re-computing are the same action. Every render re-runs every chunk, whether the analysis changed or not
Nothing in your document changed! The ground under it did:
A package upgrade changes a default
An API hands you today’s data, not October’s
A random draw with no seed
Anything that reads the clock or today’s date
A different machine with different versions
What we want is controlled re-execution: code runs again when the source changes, and stays put otherwise
freeze: re-run only when the source changesPut this in _quarto.yml for a project:
Or in the YAML header of a single document:
_freeze/freeze: auto: change the code, get new results. Change nothing, get the same resultsfreeze: true: never re-run. For archival work, a submitted paper or a signed-off reportfreeze: false: the default, always re-run_freeze/. It travels with the project, so whoever clones your repository gets your numbersfreeze does and does not doWhat it does
What it does not do
_freeze/pandas to the version you testedSo freeze postpones the problem. The rest of the answer is to pin the environment, which is module 08: uv and containers. Until then, use freeze: auto, commit _freeze/, and write your versions in the README
.qmd can also become a PDF handoutThe YAML picks the format, and the headings do the rest:
# starts a section, ## starts a new slideembed-resources: true bakes images and fonts into one self-contained file::: fenced divs handle layout: columns, centred text, font sizesInstall the course template with:
Things to try on your own:
.qmd and render it locallytheme: moon, theme: serif):::{.columns}{.smaller} class to a slide with a lot of textTwo options, both free:
| Method | How it works | When to use |
|---|---|---|
| GitHub Pages | Enable in repo settings, serves from a branch | Permanent hosting, custom domain |
| Githack | Paste the GitHub link to any .html file |
Quick sharing, no setup |
You met GitHub Pages in module 02. Githack is the fastest path: push your HTML, paste the file URL at raw.githack.com, share the link it returns
Every Quarto website is a folder with the same skeleton:
| File | Purpose |
|---|---|
_quarto.yml |
Site config: title, navigation, theme |
index.qmd |
Home page (required) |
*.qmd |
Other pages: about, posts, docs |
styles.css |
CSS overrides (optional) |
.qmd becomes one page, with the navigation, footer, and theme inherited from _quarto.ymlFour steps, clockwise from top left:
Ctrl+Shift+P (or Cmd+Shift+P), then run Quarto: Create Project_quarto.yml, index.qmd, about.qmd, and styles.css. Click Preview to see the siteClick any screenshot to zoom in
_quarto.ymlindex.qmd is hereproject:
type: website
output-dir: docs
website:
title: "DATASCI 350"
repo-url: https://github.com/danilofreire/datasci350
navbar:
left:
- href: syllabus.qmd
text: Syllabus
- href: lectures/lectures.qmd
text: Lectures
- href: assignments/assignments.qmd
text: Assignments
page-footer:
left: "Copyright 2026, Danilo Freire."
format:
html:
theme:
light: lumen
dark: solar
toc: trueQuarto has a built-in publish command for GitHub Pages:
gh-pages branch, pushes it, and GitHub serves the result at https://username.github.io/repo-name/quarto render, commit the output folder, push, and point Pages at it in the repository settings.qmd, run the command againPlain text in, public website out!
parameters-P in the terminal:-P does nothingreport.qmd---
title: "Country profile"
format: html
jupyter: python3
---
```{python}
#| tags: [parameters]
country = "Brazil"
```
```{python}
#| echo: false
import pandas as pd
import matplotlib.pyplot as plt
profiles = pd.read_csv("data/country_profiles.csv")
one = profiles[profiles["country"] == country]
one = one.sort_values("year")
```
# `{python} country`
This report uses indicators for `{python} country`.
## Life expectancy over time
```{python}
#| echo: false
fig, ax = plt.subplots(figsize=(7, 3.5))
ax.plot(one["year"], one["life_expectancy"])
plt.show()
```data/country_profiles.csv`{python} country` is inline code. It drops the value of a Python expression straight into your prose-P, and the whole document followsThis is the whole trick. One file, one source of truth, many outputs
Rendered with the default, country = "Brazil":
| Year | GDP per capita (US$) | Life expectancy |
|---|---|---|
| 2019 | 9,030 | 75.8 |
| 2020 | 7,074 | 74.5 |
| 2021 | 7,972 | 73.0 |
| 2022 | 9,281 | 74.9 |
| 2023 | 10,378 | 75.8 |
Two of the ten countries in the file. The report draws one at a time, and the parameter decides which
One country at a time, each with its own file name:
Or let the shell do all of them, with the loop you met in module 02:
Terminal
Three commands you did not have to type, and three files:
--output matters. Without it, every render writes over report.htmlThis is how one analysis becomes a pipeline!
And it is the shape of your final project: one repository, one analysis, many countries
You do not have to write the report. Download it and drive it from the terminal.
report.qmd and country_profiles.csv (click on the links to get the files)report.qmd in a new folder. Put the CSV in a data folder beside itOr download both from the terminal:
Terminal
Hints:
-P, and it takes name:value--output, your second render writes over the firstThree failures you will meet this term, as they appear on screen:
Terminal
l.172 is a line in the generated .tex, not in your .qmdWhich Python is Quarto actually running? Ask it:
Terminal
Compare that path with which python3. If they differ, you have found your error. Three fixes:
Terminal
ModuleNotFoundError for a package you know you installedQUARTO_PYTHON first, if it is set. Otherwise whatever python3 means on your PATHjupyter kernelspec list shows your kernels. Add the current environment with:In VS Code, the Render button runs in its terminal, not yours. The kernel you picked in a notebook does not follow it there
Take stock for a moment. As of today, you can build:
_freeze/ folder that pins your results until you decide to change themquarto publish gh-pagesAnd you know why each one earns its place:
freeze: auto separates “I changed the code” from “the world changed”@ references mean nothing is numbered by hand_quarto.yml in itThat is the toolkit your final project is built with. Module 06 adds scripted data collection, and module 08 pins the environment underneath. You’re ready to go!
ollama pull llama3.2:1b. The download is 1.3 GB, and the classroom wifi cannot do that twenty-five times at once-PThe complete practice.qmd:
---
title: "My Quarto Document"
subtitle: "A simple example"
author: "Danilo Freire"
date: "2026-09-30"
format: html
bibliography: references.bib
---
# Introduction
This is a simple Quarto document.
This is @fig-sine.
```{python}
#| echo: true
#| eval: true
#| fig-cap: "Sine function"
#| label: fig-sine
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
y = np.sin(x)
plt.plot(x, y)
plt.xlabel("x")
plt.ylabel("sin(x)")
plt.title("Figure 01")
plt.show()
```Render with:
The rendered output:
Key points:
#| label: fig-sine gives the figure a cross-reference label@fig-sine in the text creates a clickable link to itbibliography: references.bib tells Quarto where to find BibTeX entries@key cites from the .bib file; the reference list is added automatically at the endYour folder should look like this before you render anything:
Steps 3 to 6, in order:
Three things worth noticing:
report.html, and it says Brazil, because that is the default in the tagged cell--output, every render writes over report.html. The Japan version would have replaced the Brazil oneSouth Africa needs quotes. Without them the shell splits the name in two, the filter matches nothing, and the render stops with a ValueErrorNothing above edits the report. You changed the output by changing the input, which is the point of a parameter
If you want to go further, change the default from Brazil to India, render with no options, and watch the same file rebuild for a different country