Code
import pandas as pd
import streamlit as st
import plotly.express
import plotly
data = {
'2024-01-31' : 1044,
'2024-02-29' : 2310,
'2024-03-31' : 518,
'2024-04-30' : -1959,
'2024-05-31' : 0,
'2024-06-30' : -1010,
'2024-07-31' : 1500,
'2024-08-31' : -15459,
'2024-09-30' : -14153,
'2024-10-31' : -12604,
'2024-11-30' : -5918,
'2024-12-31' : -3897
}
df = pd.DataFrame(data.items(), columns=['date', 'value'])
fig = plotly.express.bar(data_frame=df, x='date', y='value')
st.plotly_chart(fig)
Issue
In this screenshot, the mouse is hovering over the rightmost bar.
The hover label says Dec 31, 2024
, which is expected.
Note that the x-axis label says Jan 2025
.
Workaround
Here's one approach to a workaround:
df['date'] = pd.to_datetime(df['date']).dt.to_period('M').dt.to_timestamp()
Code:
import pandas as pd
import streamlit as st
import plotly.express
import plotly
data = {
'2024-01-31' : 1044,
'2024-02-29' : 2310,
'2024-03-31' : 518,
'2024-04-30' : -1959,
'2024-05-31' : 0,
'2024-06-30' : -1010,
'2024-07-31' : 1500,
'2024-08-31' : -15459,
'2024-09-30' : -14153,
'2024-10-31' : -12604,
'2024-11-30' : -5918,
'2024-12-31' : -3897
}
df = pd.DataFrame(data.items(), columns=['date', 'value'])
df['date'] = pd.to_datetime(df['date']).dt.to_period('M').dt.to_timestamp()
fig = plotly.express.bar(data_frame=df, x='date', y='value')
st.plotly_chart(fig)
Now the x-axis labels match the data bar months:
Notes
The workaround here basically changes each date value from being the month-end to the first of the month.
So, instead of this
>>> df
date value
0 2024-01-31 1044
1 2024-02-29 2310
2 2024-03-31 518
3 2024-04-30 -1959
4 2024-05-31 0
5 2024-06-30 -1010
6 2024-07-31 1500
7 2024-08-31 -15459
8 2024-09-30 -14153
9 2024-10-31 -12604
10 2024-11-30 -5918
11 2024-12-31 -3897
we have this:
>>> df
date value
0 2024-01-01 1044
1 2024-02-01 2310
2 2024-03-01 518
3 2024-04-01 -1959
4 2024-05-01 0
5 2024-06-01 -1010
6 2024-07-01 1500
7 2024-08-01 -15459
8 2024-09-01 -14153
9 2024-10-01 -12604
10 2024-11-01 -5918
11 2024-12-01 -3897
The change is made with this line:
df['date'] = pd.to_datetime(df['date']).dt.to_period('M').dt.to_timestamp()
Question
Is this the recommended approach to resolving this issue? Or is there a more idiomatic method using plotly?
Code
import pandas as pd
import streamlit as st
import plotly.express
import plotly
data = {
'2024-01-31' : 1044,
'2024-02-29' : 2310,
'2024-03-31' : 518,
'2024-04-30' : -1959,
'2024-05-31' : 0,
'2024-06-30' : -1010,
'2024-07-31' : 1500,
'2024-08-31' : -15459,
'2024-09-30' : -14153,
'2024-10-31' : -12604,
'2024-11-30' : -5918,
'2024-12-31' : -3897
}
df = pd.DataFrame(data.items(), columns=['date', 'value'])
fig = plotly.express.bar(data_frame=df, x='date', y='value')
st.plotly_chart(fig)
Issue
In this screenshot, the mouse is hovering over the rightmost bar.
The hover label says Dec 31, 2024
, which is expected.
Note that the x-axis label says Jan 2025
.
Workaround
Here's one approach to a workaround:
df['date'] = pd.to_datetime(df['date']).dt.to_period('M').dt.to_timestamp()
Code:
import pandas as pd
import streamlit as st
import plotly.express
import plotly
data = {
'2024-01-31' : 1044,
'2024-02-29' : 2310,
'2024-03-31' : 518,
'2024-04-30' : -1959,
'2024-05-31' : 0,
'2024-06-30' : -1010,
'2024-07-31' : 1500,
'2024-08-31' : -15459,
'2024-09-30' : -14153,
'2024-10-31' : -12604,
'2024-11-30' : -5918,
'2024-12-31' : -3897
}
df = pd.DataFrame(data.items(), columns=['date', 'value'])
df['date'] = pd.to_datetime(df['date']).dt.to_period('M').dt.to_timestamp()
fig = plotly.express.bar(data_frame=df, x='date', y='value')
st.plotly_chart(fig)
Now the x-axis labels match the data bar months:
Notes
The workaround here basically changes each date value from being the month-end to the first of the month.
So, instead of this
>>> df
date value
0 2024-01-31 1044
1 2024-02-29 2310
2 2024-03-31 518
3 2024-04-30 -1959
4 2024-05-31 0
5 2024-06-30 -1010
6 2024-07-31 1500
7 2024-08-31 -15459
8 2024-09-30 -14153
9 2024-10-31 -12604
10 2024-11-30 -5918
11 2024-12-31 -3897
we have this:
>>> df
date value
0 2024-01-01 1044
1 2024-02-01 2310
2 2024-03-01 518
3 2024-04-01 -1959
4 2024-05-01 0
5 2024-06-01 -1010
6 2024-07-01 1500
7 2024-08-01 -15459
8 2024-09-01 -14153
9 2024-10-01 -12604
10 2024-11-01 -5918
11 2024-12-01 -3897
The change is made with this line:
df['date'] = pd.to_datetime(df['date']).dt.to_period('M').dt.to_timestamp()
Question
Is this the recommended approach to resolving this issue? Or is there a more idiomatic method using plotly?
Share Improve this question asked Feb 4 at 16:54 dharmatechdharmatech 9,53710 gold badges48 silver badges105 bronze badges2 Answers
Reset to default 2The dates you're using are all the last days of each month (e.g., 2024-01-31, 2024-02-29, etc.), which can cause the x-axis to shift slightly, especially when formatting. Try adjusting the date to the month start instead.
import pandas as pd
import plotly.express as px
data = {
'2024-01-31' : 1044,
'2024-02-29' : 2310,
'2024-03-31' : 518,
'2024-04-30' : -1959,
'2024-05-31' : 0,
'2024-06-30' : -1010,
'2024-07-31' : 1500,
'2024-08-31' : -15459,
'2024-09-30' : -14153,
'2024-10-31' : -12604,
'2024-11-30' : -5918,
'2024-12-31' : -3897
}
df = pd.DataFrame(data.items(), columns=['date', 'value'])
# convert to datetime
df['date'] = pd.to_datetime(df['date'])
# Adjust the dates to the first day of the month
df['date'] = df['date'].dt.to_period('M').dt.start_time
fig = px.bar(df, x='date', y='value')
fig.show()
You can also try the following if you want to preserve the hover data but update the x-axis labels
# Convert 'date' column to datetime format
df['date'] = pd.to_datetime(df['date'])
# Create the bar plot
fig = px.bar(df, x='date', y='value')
# Store original 'date' column in customdata for hover
fig.update_traces(
customdata=df['date'] # Store the original date for hover text
)
# Update x-axis to show only month and year
fig.update_xaxes(
tickvals=df['date'],
ticktext=df['date'].dt.strftime('%b %Y') # Format: abbreviated month and full year (e.g., Jan 2024)
)
# Customize hover template to show the original date (from customdata)
fig.update_traces(
hovertemplate="<b>Date:</b> %{customdata|%b %d, %Y}<br><b>Value:</b> %{y}<extra></extra>"
)
fig.show()
The first proposal allows specifying the range of the x-axis, so setting range_x
will change the scale of the x-axis that is automatically adjusted. This explanation can be found here.
import plotly.express as px
fig = px.bar(data_frame=df, x='date', y='value', range_x=['2024-01-31','2024-12-31'])
fig.show()
My second suggestion is to customize the display of the x-axis. To make it equivalent to the x-axis in the question, change the display criteria to 'M2'. There is a ticklabelmode, and there is an instant and a period. Changing this to 'period' will set the string between the scales. See here for details. The rest is adjusting the position of the scale. This adjustment needs to be adjusted depending on the environment in which it will be used.
fig = px.bar(data_frame=df, x='date', y='value')
fig.update_xaxes(dtick="M2", tickformat="%b %Y", ticklabelmode="period", ticklabelshift=40)
fig.show()