Search code examples
pythonstreamlitaltair

Set x-axis scale for Altair bar chart


I am using Python's Altair and Streamlit to create a bar chart. I'd like to have the x-axis represent time, and the y-axis be a list of people. Start and end times are plotted on the chart for each person. The problem is, I am unable to figure out how to set the scale of the x-axis. I would like the scale to start at "0:00" (midnight) and end after 24 hours (or rather 23:59:59). I have attempted many different ways, but am unable to discover how to set the scale. How can I achieve my desired scale of 0 to 24 hours?

The chart looks fine when I do not specify the scale - except that the x-axis doesn't go from 0 to 24 hours.

Chart

Here is my sample code:

import pandas as pd
import streamlit as st
import altair as alt

sample_df = pd.DataFrame({
    "Name": ["Abby", "Cathy", "Ellen", "Holly"],
    "Start time": [
        "1900-01-01 09:00:00",
        "1900-01-01 09:30:00",
        "1900-01-01 10:00:00",
        "1900-01-01 10:00:00"
    ],
    "End time": [
        "1900-01-01 10:00:00",
        "1900-01-01 10:30:00",
        "1900-01-01 11:00:00",
        "1900-01-01 11:00:00"
    ],
})

x_axis = alt.Axis(format="%-H:%M", tickCount=24)

c = (
   alt.Chart(sample_df)
   .mark_bar()
   .encode(
        x=alt.X("Start time", type="temporal", timeUnit="hoursminutes", axis=x_axis).title("Time (24-hour format)"),
        x2=alt.X2("End time", timeUnit="hoursminutes"),
        y=alt.Y('Name:N').title(None)
   )
)

st.altair_chart(c, use_container_width=True)

I tried using this code to set the scale, but it just makes the x-axis disappear in the chart. Is there another way? Or am I doing something wrong?

scale=alt.Scale(domain=[pd.Timestamp("1900-01-01 00:00:00"), pd.Timestamp("1900-01-01 23:59:59")])

Solution

  • Answering this for others who may have the same problem in the future...

    Apparently, using altair's DateTime class yields better results for time-based scales in charts. I was not successful when using Python's native datetime nor Panda's to_datetime.

    The code that works for this situation is added as a parameter to the alt.X(): scale=alt.Scale(domain=(alt.DateTime(hours=0, minutes=0), alt.DateTime(hours=23, minutes=59)))