20

It seems like you can't add a subtitle yet to a title on a graph made using the Altair Python library.

I love Altair, but according to the threads I've found Altair doesn't have a subtitling capability for a graph. Has anyone figured out how to add a subtitle? I thought of line breaks, but it looks like support for that is still getting added to Vega/Vega-lite, which is what Altair is based on.

Here's everything that I think can be found on this narrow issue...

Here is the Altair team saying it's a Vega issue:
https://github.com/altair-viz/altair/issues/987

Here is the Vega team saying it's not fixed yet (I think):
https://github.com/vega/vega-lite/issues/4055

If you can find any way to add a subtitle to either a title or an axis label, that would be huge!!

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
George Hayward
  • 485
  • 5
  • 12

3 Answers3

32

One of the best things about about the altair/vega-lite/vega ecosystem is how active it is. Since the last posting there have been a number of developments across the tool chain (this pr in particular) that have addressed exactly this issue!!

That change also adds multi-line support for titles, in addition to multiline subtitles. Example code snippet:

import altair as alt
from vega_datasets import data

chart = alt.Chart(data.cars.url).mark_point().encode(
    x='Horsepower:Q',
    y='Miles_per_Gallon:Q',
).properties(
    title={
      "text": ["First line of title", "Second line of title"], 
      "subtitle": ["Cool first line of subtitle", "Even cooler second line wow dang"],
      "color": "red",
      "subtitleColor": "green"
    }
)
chart

Which yields:

enter image description here

mcnutt
  • 663
  • 8
  • 12
10

Altair does not support subtitles, because Vega-Lite, the library that renders Altair charts, does not support subtitles.

That said, you can hack together something like a subtitle using chart concatenation, if you wish. For example:

import altair as alt
from vega_datasets import data
cars = data.cars()

title = alt.Chart(
    {"values": [{"text": "The Title"}]}
).mark_text(size=20).encode(
    text="text:N"
)

subtitle = alt.Chart(
    {"values": [{"text": "Subtitle"}]}
).mark_text(size=14).encode(
    text="text:N"
)

chart = alt.Chart(cars).mark_point().encode(
  x='Horsepower',
  y='Miles_per_Gallon',
  color='Origin'
)

alt.vconcat(
    title,
    subtitle,
    chart
).configure_view(
    stroke=None
).configure_concat(
    spacing=1
)

enter image description here

jakevdp
  • 77,104
  • 11
  • 125
  • 160
  • Thank you so much, @jakevdp! I am deeply inspired by your work on Altair. – George Hayward Jul 29 '19 at 18:47
  • 1
    Hi, @jakecdp, one question as a follow-up...what's Altair's default font? I looked it up online and in the documentation, but I didn't see it. I ask because as a Plan Z you can always add/tweak things in Photoshop or Indesign. Thank you so much for all your help! – George Hayward Jul 30 '19 at 03:16
  • 1
    The default font family is sans-serif, and the actual font used in the chart rendering is controlled by your browser's settings. – jakevdp Jul 30 '19 at 04:22
  • Thank you so much!! And thanks for all your work on this! – George Hayward Jul 30 '19 at 17:51
9

You could also use alt.TitleParams instead of creating the dictionary manually and set the title in Chart directly instead of using the .properties method:

import altair as alt
from vega_datasets import data


chart_title = alt.TitleParams(
    "Main figure title",
    subtitle=["First line that will not wrap no matter how much text it has", "Second line"],
)
alt.Chart(data.cars.url, title=chart_title).mark_point().encode(
    x='Horsepower:Q',
    y='Miles_per_Gallon:Q'
)

enter image description here

If you print the chart_title variable, you will see that it contains a dictionary similar to that used in mcnutt's earlier answer.

TitleParams({
  subtitle: ['First line that will not wrap no matter how much text it has', 'Second line'],
  text: 'Main figure title'
})

You can also use this technique to add a caption-like element under the chart:

chart_title = alt.TitleParams(
    "Main figure title",
    subtitle=["First line that will not wrap no matter how much text it has", "Second line"],
    anchor='start',
    orient='bottom',
    offset=20
)
alt.Chart(data.cars.url, title=chart_title).mark_point().encode(
    x='Horsepower:Q',
    y='Miles_per_Gallon:Q'
)

enter image description here

If we wanted to create a long caption, it would be quite tedious to format the string into a list manually. Instead we can leverage the textwrap library:

from textwrap import wrap

# Inside alt.TitleParams
subtitle=wrap("First line that will not wrap no matter how much text it has unless we convert it to a list first", 40),

enter image description here

joelostblom
  • 43,590
  • 17
  • 150
  • 159