Quarto: Notebooks for demos, presentations and web pages

Quentin Bammey

9th of February, 2022

How to present your work?

Powerpoint, Google slides, \(\ldots\)

  • User-friendly
  • Can make animations
  • Hard to make things right
  • Consistency issues: across slides, and across versions
  • Very generic, not targeted towards maths, data science, \(\ldots\)

Beamer

  • All the power of \(\LaTeX\): great for maths presentations (is it?)
  • Customizable ad libitum → but some learning curve, takes time to make
  • Static pdf: suited for presentations?

What we want in a presentation

  • Eat a cake
    • Suited for what we want to present
    • Good-looking, customizable results
    • Consistent layout across slides that helps, not constrain, your presentation1
    • Portable
    • Suited for maths, data science, computer science
  • and keep it
    • Quick and easy to create
    • No duplicate work

No duplicate work?

Convey the results of experiments (Jupyter notebook) with multiple medias:

  • Article (\(\LaTeX\))
  • Presentation(s) (Beamer, Powerpoint, …)
  • Web page or github page for the project (HTML, markdown, …)
  • IPOL demo (JSON)

Suited for what we present

import numpy as np
import matplotlib.pyplot as plt

r = np.arange(0, 2, 0.01)
theta = 2 * np.pi * r
fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
ax.plot(theta, r)
ax.set_rticks([0.5, 1, 1.5, 2])
ax.grid(True)
plt.show()

\[\frac{n!}{k!(n-k)!} = \binom{n}{k}\]

Why interactive?

Great article on the subject here

Introducing Quarto

Warning: this is not a tutorial

  • Quarto itself is very new (October 2020)
  • Exploratory work, and invitation to explore with me
  • Already easy and practical for most usage

Jupyter+Python→HTML, and more

See here

Core ideas

  • Start from a notebook (usually Jupyter)
  • Most of the content is in markdown
  • Easily display your code and its results
  • Portable: convert the notebook into a presentation, a web page, a pdf, a MS Word document, …

Getting started

  • Install Quarto
  • Define your presentation at the top of your notebook
  • Presentation: Enriched markdown, use header for slides

Organize the slide in columns

# Helix equation
t = np.linspace(0, 20, 100)
x, y, z = np.cos(t), np.sin(t), t
fig = go.Figure(data=[go.Scatter3d(
    x=x,
    y=y,
    z=z,
    mode='markers',
    marker=dict(
        size=12,
        color=z,                # set color to an array/list of desired values
        colorscale='Viridis',   # choose a colorscale
        opacity=0.8
    )
)])
# tight layout
fig.update_layout(margin=dict(l=0, r=0, b=0, t=0))
fig.show()

Suited for what we present

import numpy as np
import matplotlib.pyplot as plt

r = np.arange(0, 2, 0.01)
theta = 2 * np.pi * r
fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
ax.plot(theta, r)
ax.set_rticks([0.5, 1, 1.5, 2])
ax.grid(True)
plt.show()

\[\frac{n!}{k!(n-k)!} = \binom{n}{k}\]

Interactivity

Limitation: matplotlib figures are not rendered as interactive!

Code

plt.plot(np.arange(100), np.arange(100)**2)
plt.show()

Plotly

Examples here

Code

from sklearn.manifold import TSNE

df = px.data.iris()

features = df.loc[:, :'petal_width']

tsne = TSNE(n_components=3, random_state=0, learning_rate='auto', init='random')
projections = tsne.fit_transform(features)

fig = px.scatter_3d(
    projections, x=0, y=1, z=2,
    color=df.species, labels={'color': 'species'}
)
fig.update_traces(marker_size=8)
fig.show()

Altair

Examples here

Compared to Plotly: interactivity needs to be specified manually, but works especially well with pandas dataframes

Code

x = np.random.normal(size=100)
y = np.random.normal(size=100)

m = np.random.normal(15, 1, size=100)

source = pd.DataFrame({"x": x, "y":y, "m":m})

# interval selection in the scatter plot
pts = alt.selection(type="interval", encodings=["x"])

# left panel: scatter plot
points = alt.Chart().mark_point(filled=True, color="black").encode(
    x='x',
    y='y'
).transform_filter(
    pts
).properties(
    width=300,
    height=300
)

# right panel: histogram
mag = alt.Chart().mark_bar().encode(
    x='mbin:N',
    y="count()",
    color=alt.condition(pts, alt.value("black"), alt.value("lightgray"))
).properties(
    width=300,
    height=300
).add_selection(pts)

# build the chart:
alt.hconcat(
    points,
    mag,
    data=source
).transform_bin(
    "mbin",
    field="m",
    bin=alt.Bin(maxbins=20)
)

Bokeh

Examples here

Code

import bokeh as bkh
import bokeh.plotting as bkp
from bokeh.io import output_notebook
output_notebook(resources=bkh.resources.Resources(mode='cdn'), hide_banner=True)
N = 500
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)

p = bkp.figure(tooltips=[("x", "$x"), ("y", "$y"), ("value", "@image")])
p.x_range.range_padding = p.y_range.range_padding = 0

# must give a vector of image data for image parameter
p.image(image=[d], x=0, y=0, dw=10, dh=10, palette="Spectral11", level="image")
p.grid.grid_line_width = 0.5

bkp.show(p)

ipyleaflet for maps

Code

from ipyleaflet import Map, Marker, basemaps, basemap_to_tiles
m = Map(
  basemap=basemap_to_tiles(
    basemaps.NASAGIBS.ModisTerraTrueColorCR, "2017-04-08"
  ),
  center=(48.712539, 362.164506),
  zoom=4
)
m.add_layer(Marker(location=(48.712539, 362.164506)))
m

And web pages, IPOL demos?

Reuse your code!

They work exactly the same way. You can re-use most of your code

Footnotes

  1. I could go on and on, and I should probably avoid doing this too often, but if this is absolutely necessary for me to make you read way more content than you should have to read in a single slide, then I want to be able to do so, even if as a result of my blabber you end up reading this useless piece of information instead of listening to me.