Writing Papers with Quarto
A very convenient way to write academic papers which include the code for all reported analyses is to use Jupyter notebooks together with Quarto. Quarto is a scientific publishing system that allows you to combine text, code, and results in a single document. Quarto renders your paper to PDF using either LaTeX or Typst. When you include tables created with MakeTables in your notebook, they will be rendered directly on the screen in the notebook (as HTML via Great Tables) but will be rendered as LaTeX or Typst tables in the final PDF document by Quarto. You can include analyses using different packages, in particular pyfixest or statsmodels or even Stata.
Jupyter Notebooks or Quarto Documents?
Quarto can render two kinds of source files, and MakeTables works the same way in both:
- Jupyter notebooks (
.ipynb) are what the example below uses. You run the notebook cell by cell and see each table right below the code that creates it. The notebook stores the outputs of all cells, and by default Quarto renders these stored outputs without running the code again, so run the notebook before rendering it (or usequarto render --execute). The YAML header with Quarto’s settings goes into a raw cell at the top. Because a notebook is a JSON file that also contains the outputs, changes are hard to read in version control such as git. - Quarto documents (
.qmd) are plain text files: markdown with the YAML header at the top and the code in chunks marked with```{python}. They contain no outputs, so Quarto runs all code each time it renders the document, which makes sure the paper always matches the code. Being plain text, they are easy to edit and to track with git. Quarto runs the Python code through Jupyter, so thejupyterpackage needs to be installed.
You can switch between the two formats at any time: quarto convert quartoExample.ipynb creates quartoExample.qmd, and quarto convert quartoExample.qmd turns it back into a notebook.
Working in VS Code or Positron
Both formats work well in VS Code and in Positron, Posit’s data science editor built on VS Code. Positron comes with Quarto already installed; in VS Code, install the Quarto extension and Quarto itself. In either editor you open the .ipynb or .qmd file, select a Python environment in which MakeTables and your estimation packages are installed, and run the cells or code chunks interactively: the tables appear as HTML below the code. The command Quarto: Preview (Ctrl+Shift+K, or Cmd+Shift+K on a Mac) renders the document to PDF and shows the result next to your code, updating it each time you save. For PDF output via LaTeX you need a LaTeX installation once, which you can install from the editor’s terminal with quarto install tinytex; Typst comes with Quarto.
PDF via LaTeX or Typst
Quarto can create the PDF in two ways: via LaTeX (output format pdf) or via Typst (output format typst), a newer typesetting system that compiles much faster and needs no LaTeX installation. MakeTables detects which format Quarto is rendering and creates each table as LaTeX or Typst code accordingly, so the code of your paper stays the same for both. In the notebook or editor you still see the tables as HTML.
You choose the format in the YAML header. You can also list both and pick one when rendering:
format:
pdf:
include-in-header:
- text: |
\usepackage{booktabs}
\usepackage{makecell}
\usepackage{threeparttable}
\usepackage{tabularx}
\usepackage{multirow}
\usepackage{array}
typst:
papersize: a4
citeproc: truequarto render paper.qmd --to pdf # PDF via LaTeX
quarto render paper.qmd --to typst # PDF via TypstWithout --to, Quarto uses the first format in the header; this also applies to Quarto: Preview in VS Code and Positron. A few things differ between the two:
- LaTeX needs some packages. The LaTeX tables of MakeTables use the packages listed in
include-in-headerabove; without them the render stops with an error such asEnvironment threeparttable undefined. Typst tables need no setup. - Citations with Typst. Typst’s own citation engine cannot read a citation style given as a URL (
csl: https://...). Addciteproc: trueto thetypstformat, as above, so that citations are processed the same way as in the LaTeX route. - Styling. Format-specific layout options are passed with
tex_styleandtypst_style, for instancemt.DTable(df, ..., tex_style={"first_col_width": "3cm"}, typst_style={"first_col_width": "3cm"}). See LaTeX and Typst for the available options.
An Example Notebook
Below is a short example paper written as a Jupyter notebook. The first tab shows the complete notebook, cell by cell: the raw cell at the top holds the YAML header with Quarto’s settings for both PDF routes, and the # | comments in the code cells (Quarto accepts them with or without the space, as #|) give each table a label (for cross-references such as @tbl-1) and a caption. The other two tabs show the PDFs that Quarto renders from it via LaTeX and via Typst.
- Download the notebook: quartoExample.ipynb (view on GitHub)
- Download the BibTeX file with the references: references.bib
- Download the rendered PDFs: quartoExample.pdf (via LaTeX) and quartoExample-typst.pdf (via Typst)
To create the PDFs yourself, put the notebook and references.bib in the same folder and run quarto render quartoExample.ipynb --to pdf (via LaTeX) or quarto render quartoExample.ipynb --to typst (via Typst). This needs Quarto, for the LaTeX route a LaTeX installation (e.g. quarto install tinytex), and the Python packages used in the notebook (maketables, pyfixest, statsmodels).
Cell 1: Raw cell (YAML header)
---
title: "Quarto and MakeTables"
format:
pdf:
keep-tex: true
papersize: a4
fontsize: 11pt
fontfamily: newpxtext
link-citations: true
linkcolor: "black"
citecolor: "black"
number-sections: true
indent: "0m"
cite-method: citeproc
include-in-header:
- text: |
\usepackage{setspace}
\usepackage{float}
\usepackage{booktabs}
\usepackage{makecell}
\usepackage{threeparttable}
\usepackage{tabularx}
\usepackage{multirow}
\usepackage{array}
typst:
papersize: a4
fontsize: 11pt
number-sections: true
citeproc: true
execute:
echo: false
warning: false
cache: false
freeze: false
bibliography: references.bib
csl: https://www.zotero.org/styles/apa
author:
- Peter Pan^[University of Nowhere]
date: "May, 2025"
date-format: long
abstract: |
This document illustrates how to use MakeTables to write academic papers. Just write your paper in markdown in Jupyter Notebooks. Tables are displayed as HTML interactively when you work in the notebook on the screen. When you render the notebook as PDF with Quarto, MakeTables generates LaTeX or Typst code, depending on the output format.
\newpage
---Cell 2: Code cell
# | echo: false
# | include: false
# | eval: true
# Import necessary libraries
import pyfixest as pf
import statsmodels.formula.api as smf
import maketables as mt
# Load your data (here using a sample Stata dataset with the import_dta function that also stores variable labels)
df = mt.import_dta(
"https://raw.githubusercontent.com/py-econometrics/maketables/main/data/auto.dta"
)Cell 3: Markdown cell
\onehalfspacing
## Introduction
A very convenient way to write academic papers which include the code for all reported analyses is to use Jupyter notebooks together with [Quarto](https://quarto.org/). Quarto is a scientific publishing system that allows you to combine text, code, and results in a single document. Quarto renders Jupyter notebooks to PDF using either LaTeX or Typst. When you include tables created with MakeTables in your notebook, they will be rendered directly on the screen in the notebook (as HTML via [Great Tables](https://posit-dev.github.io/great-tables/articles/intro.html)) but will be rendered as LaTeX or Typst tables in the final PDF document by Quarto.
Here is simple example of how to use MakeTables in a notebook to write a paper. It generates tables, for instance based on regressions in either [pyfixest](https://py-econometrics.github.io/pyfixest/pyfixest.html) or [statsmodels](https://www.statsmodels.org/stable/index.html). Render this notebook with `quarto render quartoExample.ipynb --to pdf` to create the PDF document via LaTeX, or with `quarto render quartoExample.ipynb --to typst` to create it via Typst.
Note it is also very easy to include references and citations in your Quarto document (see for instance @wooldridge2010econometric and @greene2018econometric).Cell 4: Markdown cell
## Some Tables
@tbl-1 shows some descriptive statistics.Cell 5: Code cell
# | label: tbl-1
# | tbl-cap: "Automotive Data Summary"
# | tbl-pos: "H"
# Create a descriptive statistics table
# Note that we can also pass through output-specific styling parameters
# Here we for instance want the first column with the variable names in LaTeX output to be slightly larger
mt.DTable(
df,
vars=["mpg", "weight", "price"],
stats=["count", "mean", "std"],
bycol=["foreign"],
tex_style={"first_col_width": "3cm"},
)Cell 6: Markdown cell
@tbl-2 shows regression results here estimated with the excellent `pyfixest` package.Cell 7: Code cell
# | label: tbl-2
# | tbl-cap: "A Regression Table (PyFixest)"
# | tbl-pos: "H"
# Fit models using pyfixest
est1 = pf.feols("mpg ~ weight", data=df)
est2 = pf.feols("mpg ~ weight + length", data=df)
# Make the table
mt.ETable([est1, est2])Cell 8: Markdown cell
@tbl-3 uses `statsmodels` instead to estimate a probit model.Cell 9: Code cell
# | label: tbl-3
# | tbl-cap: "A Regression Table (Statsmodels)"
# | tbl-pos: "H"
# Generate a dummy variable and label it
df["foreign_i"] = (df["foreign"] == "Foreign") * 1
mt.set_var_labels(df, {"foreign_i": "Foreign (indicator)"})
# Fit your models
est1 = smf.ols("foreign_i ~ weight + length + price", data=df).fit()
est2 = smf.probit("foreign_i ~ weight + length + price", data=df).fit(disp=0)
# Make the table
mt.ETable(
[est1, est2], model_stats=["N", "r2", "pseudo_r2"], model_heads=["OLS", "Probit"]
)Cell 10: Markdown cell
## Conclusion
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
{{< pagebreak >}}
## References
::: {#refs}
:::