title | date | tags |
---|---|---|
Making Altair/Vega-Lite charts readable without squinting |
2019-10-16 20:14 +1100 |
web python |
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:
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.
{:.hl-para}
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.
Footnotes
-
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. ↩