16 Oct '19
Making Altair/Vega-Lite charts readable without squinting
My love for the Grammar of
Graphics
runs deep, and in particular for Hadley Wickham’s famous ggplot2
which showed
me the light back when I was a young PhD student. Seriously, once you have your
head around how it works it gives you datavis superpowers. These days I often
work in Python, and for datavis I’m enjoying
Altair which is based around the same
philosophy (and outputs to Vega-Lite for
rendering in the browser).
Recently I’ve needed to (a) create some snazzy graphs with Altair and (b) display them in a slide deck. Part (a) was actually the easy part—the tricky part was (b) getting Altair to render charts with text & other marks that weren’t so small that the slide was unreadable.
Here’s an example: a simple line chart from the Altair Example Gallery.
import altair as alt
import numpy as np
import pandas as pd
x = np.arange(100)
source = pd.DataFrame({"x": x, "f(x)": np.sin(x / 5)})
alt.Chart(source).mark_line().encode(x="x", y="f(x)").save(f"{CHART_DIR}/sin-x.svg")
which (with default settings) produces a chart that looks like this:
Easy tweaking of “size” through chart themes
Now, that figure might look fairly readable, but when it’s on a slide the text, labels & even lines are quite small1. I don’t need fine-grained control over the relative sizes of labels vs legend vs title, etc. I just want a simple knob for making all the text bigger so that my slides don’t double as an eye chart. The Vega-Lite folks (the underlying vis engine which Altair uses) know about the issue, but don’t want to fix it.
Note that when I’m talking about “size” I’m not talking about the size & dimensions of the chart—I’m talking about the size of the text, lines & other marks relative to the overall size of the chart.
The easiest way I found to fix this is to set a small width & height for the
chart, then export to a vector format (e.g. svg) so that when the image gets
displayed everything will be “stretched” up into big, bold sizes (and since it’s
a vector format, things will still be nice and crisp). This chart code is the
same except for the .properties(width=100, height=60)
part:
alt.Chart(source).mark_line().encode(x="x", y="f(x)").properties(
width=100, height=60
).save(f"{CHART_DIR}/sin-x-big-text.svg")
Obviously I’m exaggerating here to make a point, but the key point is that there
are just a couple of numbers to tweak (width
and height
) which control text
& line size, label sizes, and also titles and legends (if present). And that’s
not something that’s exposed as simply in any other way by the Altair/Vega-Lite
API.
One final tip: if you want to have consistent sizes & aspect ratios across lots
of charts (e.g. you’re batch exporting lots of charts for a presentation or
report) you can create a custom
theme,
but otherwise you can just do it with a call to the .properties()
method as
shown.
-
To be honest, these simple examples from the example gallery don’t really help me make my point, they’re still pretty readable. But when the charts get more complicated & have more data marks then things get smaller & more zoomed out, and the problem gets much worse. ↩