Search code examples
pythonaltairvega-lite

Changing the width of a step in Altair line and area charts


I try to build a line chart that displays a value for each month of a full year. I also want to fill months that exceed a threshold. I have an issue with the appearance of the last values of both the line and the fill.

import altair as alt
from vega_datasets import data

source = data.stocks()
year_data = source[source.date.dt.year == 2007]

line = alt.Chart(year_data, width=600).mark_line(
    interpolate='step-after',
    color='red'
).encode(
    x='date',
    y='price'
).transform_filter(alt.datum.symbol == 'IBM')

fill = alt.Chart(year_data, width=600).mark_area(
    interpolate='step-after',
).encode(
    x='date',
    y='price',
).transform_filter(
    (alt.datum.symbol == 'IBM') &
    (alt.datum.price > 105)
)

fill + line

line chart for stock values

  1. How can I display December with the same width as the other months so it does not get chopped off visually?
  2. October also exceeds the threshold of 105 but doesn't appear to be filled. What can I do to fill it like the other months?

Solution

  • I think your issue with the extent of the step interpolation is similar to what is described in Drawing line plot for a histogram and I am not sure there is a good solution.

    A workaround would be to perform temporal binning to convert your data to ordinal and use a bar mark instead of an area:

    import altair as alt
    from vega_datasets import data
    
    
    source = data.stocks()
    year_data = source[source.date.dt.year == 2007]
    
    line = alt.Chart(year_data, width=600).mark_line(
        interpolate='step-after',
        color='red'
    ).encode(
        x='month(date)',
        y='price'
    ).transform_filter(
        alt.datum.symbol == 'IBM'
    )
    
    line.mark_bar().transform_filter(alt.datum.price > 105) + line
    

    enter image description here

    You can also make the x-axis ordinal to avoid Dec getting chopped off.

    line = alt.Chart(year_data, width=600).mark_line(
        interpolate='step',
        color='red'
    ).encode(
        x='month(date):O',
        y='price'
    ).transform_filter(
        alt.datum.symbol == 'IBM'
    )
    
    line.mark_bar().transform_filter(alt.datum.price > 105) + line
    

    enter image description here