ejain
ejain

Reputation: 3614

How to plot y-axis bands in Altair charts?

Can Altair plot bands on the y axis, similar to this Highcharts example?

The docs have an example showing how to draw a line on the y axis, but adapting the example to use plot_rect to draw a band instead doesn't quite work:

import altair as alt
from vega_datasets import data

weather = data.seattle_weather.url

chart = alt.Chart(weather).encode(
    alt.X("date:T")
)

bars = chart.mark_bar().encode(
    y='precipitation:Q'
)

band = chart.mark_rect().encode(
    y=alt.value(20),
    y2=alt.value(50),
    color=alt.value('firebrick')
)

alt.layer(bars, band)

plot_rect with fixed y and y2

Upvotes: 1

Views: 2031

Answers (1)

FlorianGD
FlorianGD

Reputation: 2436

I think the problem when you give a value with alt.value is that you specify the value in pixels starting from the top of the graph : it is not mapped to the data.

In the initial answer, with mark_rule, it would'nt create a clean band but a lot of vertical stripes, so here is a way to correctly plot a band.

First solution is to create a brand new data frame for the band, and layer that on top of the bars:

import altair as alt
import pandas as pd
from vega_datasets import data

weather = data('seattle_weather')
band_df = pd.DataFrame([{'x_min': weather.date.min(),
                         'x_max': weather.date.max(),
                         'y_min': 20,
                         'y_max': 50}])

bars = alt.Chart(weather).mark_bar().encode(
    x=alt.X('date:T'),
    y=alt.Y('precipitation:Q', title="Precipitation")
)

band_2 = alt.Chart(band_df).mark_rect(color='firebrick', opacity=0.3).encode(
    x='x_min:T',
    x2='x_max:T',
    y='y_min:Q',
    y2='y_max:Q'
)

alt.layer(bars, band_2)

enter image description here

Second option, if you do not want/cannot create a dataframe, is to use transform_calculate, and manually specify x and x2 in the band chart:

bars = alt.Chart().mark_bar().encode(
    x=alt.X('date:T', title='Date'),
    y=alt.Y('precipitation:Q', title="Precipitation")
)

band_3 = alt.Chart().mark_rect(color='firebrick', opacity=0.3).encode(
    x='min(date):T',
    x2='max(date):T',
    y='y_min:Q',
    y2='y_max:Q'
).transform_calculate(y_min='20', y_max='50')

alt.layer(bars, band_3, data=data.seattle_weather.url)

enter image description here

Initial answer

I would do 2 things to mimic the highchart example you gave. First, use a transform_calculate to set y_min and y_max values. And second, I'll use mark_rule so that the band span on the X axis where there are values. (I also added some opacity and changed the order of the layers so that the band is behind the bars.)

import altair as alt
from vega_datasets import data

weather = data.seattle_weather.url

chart = alt.Chart().encode(
    alt.X("date:T")
)

bars = chart.mark_bar().encode(
    y='precipitation:Q'
)

band = chart.mark_rule(color='firebrick',
                       opacity=0.3).encode(
    y=alt.Y('y_min:Q'),
    y2=alt.Y('y_max:Q')
).transform_calculate(y_min="20",
                      y_max="50")


alt.layer(band, bars, data=weather)

altair_plot_with_band

Upvotes: 3

Related Questions