A year of using Quarto with Julia

Tips and tricks for Julia practitioners — Julia Eindhoven

Patrick Altmeyer
using Pkg
Pkg.activate("")

Blurb

As a Julia practitioner you may want to publish your work in various forms: notebooks, Markdown, HTML, PDF and more. What if you could produce all these different outputs from the same input? I will share how I’ve been using Quarto with Julia, for package documentation, blogging and JuliaCon proceedings.

DISCLAIMER: Views presented in this presentation are my own. I am not affiliated with either Quarto or Posit (RStudio).

Quick Intro

  • Currently 2nd year of PhD in Trustworthy Artificial Intelligence at Delft University of Technology.
  • Working on Counterfactual Explanations and Probabilistic Machine Learning with applications in Finance.
  • Previously, educational background in Economics and Finance and two years at the Bank of England.
  • Enthusiastic about free open-source software, in particular Julia and Quarto.

What is Quarto?

From R Markdown …

  • R Markdown users have enjoyed many of the benefits highlighted in today’s for many years.
  • For me personally, the workflow enabled by R Markdown was for many years a key reason to rely on R whenever possible (see here).
  • In recent years Posit (formerly RStudio) has first embraced Python and then geared towards multi-language support.

… to Quarto

  • Generate multiple different output formats with ease:
    • The old school: \(\LaTeX\) and PDF (including Beamer); MS Office
    • The brave new world: beautiful, dynamic HTML content
      • websites
      • e-books
      • apps
  • All of this starting from the same place …

Quarto at JuliaCon 2022.

Quarto at JuliaCon 2022.

A plain Markdown document blended with your favorite programming language and a YAML header defining your output.

Effective Communication and Reproducibility in Science

  • Most science today involves code. Often code forms such an integral part of the science, that it deserves its place in the final publication.
  • Scientific Ideas can often be most effectively communicated through dynamic visualizations.
  • Requirements and preferences vary.
  • Quarto allows us to cater to those needs, while at the same time facilitating reproducibility by bridging the gap between computations and writing.

Quarto enables effective communication and reproducibility without compromises.

Code chunks

Most science today involves code.

using Markdown
Markdown.parse("""
Often code forms such an integral part of the science, that it deserves its place in the final publication.
""")

Often code forms such an integral part of the science, that it deserves its place in the final publication.

Using simple YAML options, we can specify how code is displayed. For example, we may want to use code folding to avoid unnecessary interruptions or hide large code chunks like this one that builds Figure 1.

Code
using Javis, Animations, Colors
www_path = "www/images"

_size = 600
radius_factor = 0.33

function ground(args...)
    background("transparent")
    sethue("white")
end

function rotate_anim(idx::Number, total::Number) 
    distance_circle = 0.875
    steps = collect(range(distance_circle,1-distance_circle,length=total))
    Animation(
        [0, 1], # must go from 0 to 1
        [0, steps[idx]*2π],
        [sineio()],
    )
end

translate_anim = Animation(
    [0, 1], # must go from 0 to 1
    [O, Point(_size*radius_factor, 0)],
    [sineio()],
)

translate_back_anim = Animation(
    [0, 1], # must go from 0 to 1
    [O, Point(-(_size*radius_factor), 0)],
    [sineio()],
)

julia_colours = Dict(
    :blue => "#4063D8",
    :green => "#389826",
    :purple => "#9558b2",
    :red => "#CB3C33"
)
colour_order = [:red, :purple, :green, :blue]
n_colours = length(julia_colours)
function color_anim(start_colour::String, quarto_col::String="#4b95d0")
    Animation(
        [0, 1], # must go from 0 to 1
        [Lab(color(start_colour)), Lab(color(quarto_col))],
        [sineio()],
    )
end

video = Video(_size, _size)

frame_starts = 1:10:40
n_total = 250
n_frames = 150
Background(1:n_total, ground)

# Blob:
function element(; radius = 1)
    circle(O, radius, :fill) # The 4 is to make the circle not so small
end

# Cross:
function cross(color="black";orientation=:horizontal)
    sethue(color)
    setline(10)
    if orientation==:horizontal
        out = line(Point(-_size,0),Point(_size,0), :stroke)
    else
        out = line(Point(0,-_size),Point(0,_size), :stroke)
    end
    return out
end

for (i, frame_start) in enumerate(1:10:40)

    # Julia circles:
    blob = Object(frame_start:n_total, (args...;radius=1) -> element(;radius=radius))
    act!(blob, Action(1:Int(round(n_frames*0.25)), change(:radius, 1 => 75))) # scale up
    act!(blob, Action(n_frames:(n_frames+50), change(:radius, 75 => 250))) # scale up further
    act!(blob, Action(1:30, translate_anim, translate()))
    act!(blob, Action(31:120, rotate_anim(i, n_colours), rotate_around(Point(-(_size*radius_factor), 0))))
    act!(blob, Action(121:150, translate_back_anim, translate()))
    act!(blob, Action(1:150, color_anim(julia_colours[colour_order[i]]), sethue()))

    # Quarto cross:
    cross_h = Object((n_frames+50):n_total, (args...) -> cross(;orientation=:horizontal))
    cross_v = Object((n_frames+50):n_total, (args...) -> cross(;orientation=:vertical))
end

render(
    video;
    pathname = joinpath(www_path, "julia_quarto.gif"),
)
Figure 1: A simple animation built with Javis.jl.

Dynamic Visualizations

Scientific Ideas can often be most effectively communicated through dynamic visualizations.

using Plots
using StatsBase

steps = randn(1)
T = 100

anim = @animate for t in 2:T
    append!(steps, randn(1))
    random_walk = cumsum(steps)
    p1 = plot(random_walk, color=1, label="", title="A Gaussian random walk ...", xlims=(0,T))
    acf = autocor(random_walk)
    p2 = bar(acf, color=1, label="", title="... is non-stationary", xlims=(0,10), ylims=(0,1))
    plot(p1, p2, size=(800,300))
end
gif(anim, fps=5)

Meeting Varying Requirements

Quarto has fantastic support for traditional and modern scholarly writing.

The challenge …

. . .

Some people still prefer to read paper or work with MS Office. Most scientific journals, for example, still work with PDF and \(\LaTeX\).

Figure 2: Source: Giphy

… and Quarto’s answer

. . .

Equations like Equation 1 (as well as Sections, Figures, Theorems, …) can be cross-referenced in a standardized way.

$$
\begin{aligned}
Z &= \sum_{t=0}^T X_t, && X_t \sim N(\mu, \sigma)
\end{aligned}
$$ {#eq-bm}

\[ \begin{aligned} Z &= \sum_{t=0}^T X_t, && X_t \sim N(\mu, \sigma) \end{aligned} \qquad(1)\]

Reproducible and Dynamic Content

Quarto allows us to cater to different requirements, while at the same time facilitating reproducibility by bridging the gap between computations and writing.

The world and the data that describes it is not static 📈. Why should scientific outputs be?

From dynamic inputs …

. . .

The code below depends on remote data that is continuously updated:

using MarketData
snp = yahoo("^GSPC")

using Dates
last_trade_day = timestamp(snp[end])[1]
p_close = values(snp[end,:Close])[1]
last_trade_day_formatted = Dates.format(last_trade_day, "U d, yyyy")

We’d like any updates to the inputs to automatically affect our output (ideally, all the way through to the finished report or paper).

… to dynamic outputs

. . .

Markdown.parse("""
When the S&P 500 last traded, on $(last_trade_day_formatted), it closed at $(p_close). 
""")

When the S&P 500 last traded, on February 23, 2023, it closed at 4012.320068.

Quarto with Julia

In this section …

Preferred setup: VSCode, Quarto and Julia

  • Using Julia and VSCode? There’s a Quarto extension for VSCode, so you can stick with your preferred IDE.

Blogging

  • Quarto makes it easy to build beautiful websites and blogs.

Julia Packages

  • Documenter.jl and Quarto play nicely with each other (both Markdown based).
  • Turning Julia packages into Quarto projects comes with a few advantages.

JuliaCon Proceedings

  • JuliaCon proceedings submissions currently work with a GitHub template that requires users to submit a .tex file.
  • Quarto can be used to generate that file, but there are additional benefits we could tab into.

VSCode, Quarto and Julia

To get started, see here.

Some tips to get started

  • Add IJulia to startup.jl

If you install a new Julia binary […], you must update the IJulia installation […] by running Pkg.build("IJulia")

— Source: IJulia docs

  • User snippets need to be explicitly enabled (see here)
{
  "Two columns": {
    "prefix": "cols",
    "body": [
      "::::{.columns}::::",
      ":::{.column width='$1%'}",
      ":::",
      ":::{.column width='$2%'}",
      ":::",
      "::::"
    ]
  }
}

. . .

Using .ipynb vs .qmd

  • Can switch between Jupyter and .qmd with ease.
  • When working with .qmd, code chunks connect to REPL.
  • Set keep-ipynb: true to have interactive notebooks in repo.

Blogging

These very slides are not only built using Quarto, but also hosted on a website that is also run on Quarto.

Organization — Quarto uses something call document listings: an easy way to collect, arrange and navigate content like this one.

---
title: "Talks"
listing:
  contents: 
    - "posts/*/index.qmd"
  sort: "date desc"
  type: default
  categories: false
  fields: [image, date, title, description, author, file-modified]
  image-align: left
---

Code Execution — You can specify YAML options such that changes to your underlying Julia code will trigger your blog post to be rerendered. This essentially allows you to easily test that the code you publish actually runs:

execute:
  freeze: auto
  eval: true
  echo: true
  output: false

Reproducibility — The Julia version and environment can be managed globally or locally for individual blog posts:

jupyter: julia-1.8
using Pkg; Pkg.activate("<path>")

Community Engagement — It is remarkably easy to engage the community through support for commenting, an RSS Feed, …

comments:
  utterances:
    repo: quarto-dev/quarto-docs

Julia Packages

  • Documenter.jl and Quarto generally play nicely with each other (both Markdown based).
format: 
  commonmark:
    variant: -raw_html
    wrap: preserve
  • You get some stuff for free, e.g. citation management. Unfortunately, still no support for cross-referencing …
  • The use of jldoctest is not always straight-forward (see here). Letting docs run through the Quarto engine provides an additional layer of quality assurance.
  • Admonitions can be used as follows (see related discussion):
| !!! note \"An optional title\"
| &nbsp; &nbsp; Here is something that you should pay attention to.   

As an example, we will look at … 🥁

LaplaceRedux.jl

Stable Dev Build Status Code Style: Blue ColPrac: Contributor’s Guide on Collaborative Practices for Community Packages Twitter Badge

LaplaceRedux.jl is a library written in pure Julia that can be used for effortless Bayesian Deep Learning trough Laplace Approximation (LA).

JuliaCon Proceedings

  • Quarto supports \(\LaTeX\) templates/classes, but I’ve found that rticles still has an edge here.
  • The list of out-of-the-box templates for journal articles is growing.

As an example we will look at … 🥁

… my pending JuliaCon Proceedings submission for my 2022 talk: Explaining Black-Box Models through Counterfactuals

  • … but why only publish proceedings in PDF form?
  • Quarto opens the floodgates to more innovative forms of publishing (think distill, but more than that)

Questions & Answers ❓

Packages I’ve built … 📦

CounterfactualExplanations.jl

Stable Dev Build Status codecov codecov 77% 77%

CounterfactualExplanations.jl is a package for generating Counterfactual Explanations (CE) and Algorithmic Recourse (AR) for black-box algorithms. Both CE and AR are related tools for explainable artificial intelligence (XAI). While the package is written purely in Julia, it can be used to explain machine learning algorithms developed and trained in other popular programming languages like Python and R. See below for short introduction and other resources or dive straight into the docs.

Turning a nine (9) into a four (4).

A sad 🐱 on its counterfactual path to its cool dog friends.

LaplaceRedux.jl

Stable Dev Build Status codecov codecov 96% 96%

JuliaCon 22: Effortless Bayesian Deep Learning through Laplace Redux

LaplaceRedux.jl is a small package that can be used for effortless Bayesian Deep Learning and Logistic Regression trough Laplace Approximation. It is inspired by this Python library and its companion paper.

Plugin Approximation (left) and Laplace Posterior (right) for simple artificial neural network.

Simulation of changing posteriour predictive distribution. Image by author.

ConformalPrediction.jl

Stable Dev Build Status codecov codecov 70% 70% Code Style: Blue ColPrac: Contributor’s Guide on Collaborative Practices for Community Packages Twitter Badge

ConformalPrediction.jl is a package for Uncertainty Quantification (UQ) through Conformal Prediction (CP) in Julia. It is designed to work with supervised models trained in MLJ (Blaom et al. 2020). Conformal Prediction is distribution-free, easy-to-understand, easy-to-use and model-agnostic.

Conformal Prediction in action: Prediction sets for two different samples and changing coverage rates. As coverage grows, so does the size of the prediction sets.

More Resources 📚

Read on …

  • Related blog posts (hosted on this website that itself is built with Quarto and involves lots of Julia content): [1] and [2].
  • Blog post introducing CE: [TDS], [blog].
  • Blog post on Laplace Redux: [TDS], [blog].
  • Blog post on Conformal Prediction: [TDS], [blog].

… or get involved! 🤗

Questions & Answers ❓

Image Sources

  • Quarto logo. Source: Quarto
  • Julia to Quarto animation. Source: author (heavily borrowing from Javis.jl tutorial)

References

Blaom, Anthony D., Franz Kiraly, Thibaut Lienart, Yiannis Simillides, Diego Arenas, and Sebastian J. Vollmer. 2020. MLJ: A Julia Package for Composable Machine Learning.” Journal of Open Source Software 5 (55): 2704. https://doi.org/10.21105/joss.02704.