最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - plotly x-axis label is offset by one month - Stack Overflow

programmeradmin1浏览0评论

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 badges
Add a comment  | 

2 Answers 2

Reset to default 2

The 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()

发布评论

评论列表(0)

  1. 暂无评论