Generating Typst Tables

Overview

Typst is a typesetting system that offers an alternative to LaTeX. A key advantage is that it offers extremely fast compiling. This notebook demonstrates how to generate publication-quality tables in Typst format using maketables.

An Example Document

Here you see the pdf of a typst document generated by the code explained in the end of this notebook:

Setup and Data Preparation

First, let’s load the necessary libraries and prepare the data:

# Import necessary libraries
import sys
import numpy as np
import pandas as pd
import pyfixest as pf
import statsmodels.formula.api as smf

# Force reload of maketables module - delete ALL maketables modules
mods_to_del = [k for k in sys.modules.keys() if 'maketables' in k]
for mod in mods_to_del:
    del sys.modules[mod]

import maketables as mt

# Load sample dataset
df = pd.read_csv("../data/salaries.csv")

# Set variable labels
labels = {
    "logwage": "ln(Wage)",
    "wage": "Wage", 
    "age": "Age",
    "female": "Female",
    "tenure": "Years of Tenure",
    "occupation": "Occupation",
    "worker_type": "Worker Type",
    "education": "Education Level",
    "promoted": "Promotion"
}

# Set default labels 
mt.MTable.DEFAULT_LABELS = labels

# Generate a categorical variable for gender from the dummy variable
df["gender"] = df["female"].map({0: "Male", 1: "Female"})

Generating Tables

Create a descriptive statistics table:

# Create descriptive statistics table
tab1 = mt.DTable(df, vars=["wage", "age", "tenure"],
                 bycol=["worker_type"], byrow="gender",
                 stats=["count", "mean", "std"],
                 caption="Descriptive statistics by worker type and gender",
                 tab_label="tab:descriptives",
                 format_spec={'mean': ',.2f', 'std': '.2f'})

# Save as Typst
tab1.save(type="typst", file_name="../output/table1_descriptives.typ", show=False, replace=True)

Create the wage regression table using PyFixest’s stepwise notation:

# Create regression table using PyFixest
tab2 = mt.ETable(pf.feols("logwage+wage ~ age + female + sw0(age:female)", data=df),
                 caption="Wage regressions",
                 tab_label="tab:regressions")

# Save as Typst
tab2.save(type="typst", file_name="../output/table2_regressions.typ", show=False, replace=True)

Now use Statsmodels for an OLS and Probit comparison:

# Fit models for promotion prediction
est1 = smf.ols("promoted ~ tenure + female + worker_type", data=df).fit()
est2 = smf.probit("promoted ~ tenure + female + worker_type", data=df).fit(disp=0)

# Create comparison table
tab3 = mt.ETable([est1, est2],
                 keep=["tenure", "female", "worker_type"],
                 model_stats=["N", "r2", "pseudo_r2"],
                 model_heads=["OLS", "Probit"],
                 caption="Predicting Promotions",
                 tab_label="tab:promotions")

# Save as Typst
tab3.save(type="typst", file_name="../output/table3_promotions.typ", show=False, replace=True)

Output Style and Defaults

You can customize table appearance by modifying the DEFAULT_TYPST_STYLE dictionary. Key parameters include:

  • first_col_width: Width of the first column (default "auto" - adapts to content)
  • column_gutter: Space between columns (default "0.5em" at MultiIndex transitions)
  • group_header_format: Format for group headers (default "%s" - plain text)
  • notes_font_size: Font size for notes (default "10pt")
  • rgroup_sep: Row group separators - "t" (top), "b" (bottom), "tb" (both), or "" (none)

Example customization:

# Adjust styling and formatting for Typst
mt.MTable.DEFAULT_TYPST_STYLE.update({
    "first_col_width": "auto",  # Auto-size to content width
    "notes_font_size": "9pt",    # Smaller notes
    "group_header_format": "*%s*",  # Bold group headers
})

Column Width Control

Control the first column width in Typst tables:

  • Default ("auto"): First column adapts to the widest row label, data columns share remaining space equally
  • Fixed width: Set first_col_width to a specific value like "3cm", "2in", or "150pt"

Example with explicit width:

# Create table with explicit first column width
tab1_fixed = mt.DTable(df, vars=["wage", "age", "tenure"],
                        bycol=["worker_type"], byrow="gender",
                        stats=["count", "mean", "std"],
                        caption="Descriptive statistics (fixed column width)",
                        format_spec={'mean': ',.2f', 'std': '.2f'})

# Save with explicit first column width
tab1_fixed.save(type="typst", file_name="../output/table1_descriptives_fixed_width.typ", 
                show=False, replace=True,
                typst_style={"first_col_width": "2.5cm"})

Typst Integration

To use these Typst tables in your documents, reference them using Typst’s include statement:

#include "table1_descriptives.typ"

Or embed directly in your Typst document source. For more information, see typst.app

Combined Typst Document

You can also use the update_typst method to create a single Typst file containing all tables. With this method you can also update table content in an existing Typst document. The method checks whether a table with the same label already exists in the document and replaces it if found; otherwise, it appends the new table at the end of the document.

# Create or update a comprehensive Typst document using update_typst
output_path = "../output/TypstOutput.typ"

# Base document with section headers and narrative text
typst_header = """#set page(paper: "a4")"""

with open(output_path, "w", encoding="utf-8") as f:
    f.write(typst_header)

# Insert or replace tables by label
tab1.update_typst(file_name=output_path, tab_label="tab:descriptives")
tab2.update_typst(file_name=output_path, tab_label="tab:regressions")
tab3.update_typst(file_name=output_path, tab_label="tab:promotions")