0

I have a time series with daily data, that I want to plot, and only plot a x-tick every month. I have tried multiple approaches (including the ones described here, but it seems that pandas considers my data from 1970, even if the date index is from another date. Example below:

date_range = pd.date_range(start='2023-01-01', periods=periods)
data = {
    'A': np.random.randint(1, 10, periods),
    'B': np.random.randint(1, 10, periods),
}
df = pd.DataFrame(data, index=date_range)
df

df['total'] = df.sum(axis=1)
df['rolling'] = df['total'].rolling(3).mean()
ax = df[['A', 'B']].plot(kind='bar', stacked=True)

# this one defaults to 1970—which seems to be a known problem
# https://stackoverflow.com/questions/69101233/using-dateformatter-resets-starting-date-to-1970
# ax.xaxis.set_major_locator(MonthLocator())
# ax.xaxis.set_major_formatter(DateFormatter('%b %Y'))

# this one also doesn't work, because the data is thought to be also from 1970 so the picture comes out very wrong
monthly_ticks = pd.date_range(start=df.index.min(), end=df.index.max(), freq='ME')
ax.set_xticks(monthly_ticks)
ax.set_xticklabels(monthly_ticks, rotation=45);```

1 Answer 1

1

The issue stems from Pandas struggling to correctly interpret your date format (yyyy-mm-dd). There's a possibility that adopting a UNIX-timestamp-ish format could work since that seems to be the default behavior (defaulting to 1st of January 1970).

Regardless, reading from the dataframe and directly using ax.bar() (as opposed to df.plot()/df[<some_data>].plot()) to create your stacked bar plots gives you the correct monthly x-ticks.


I've used random y-axis data across the whole year of 2023 (365 data points each for A and B). I've also slightly rotated the ticks to prevent textual overlap.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter, MonthLocator

periods = 365  # example for one year of daily data
date_range = pd.date_range(start='2023-01-01', periods=periods)
data = {
    'A': np.random.randint(1, 10, periods),
    'B': np.random.randint(1, 10, periods),
}
df = pd.DataFrame(data, index=date_range)

df['total'] = df.sum(axis=1)
df['rolling'] = df['total'].rolling(3).mean()

# Favor plt.bar over df.plot
fig, ax = plt.subplots(figsize=(10, 6))
ax.bar(df.index, df['A'], label='A', width=1)
ax.bar(df.index, df['B'], bottom=df['A'], label='B', width=1)

# Month interval formatting
ax.xaxis.set_major_locator(MonthLocator())
ax.xaxis.set_major_formatter(DateFormatter('%b %Y'))

# Slight rotation
plt.setp(ax.get_xticklabels(), rotation=45, ha='right')

ax.legend()
plt.show()

Output:

Stacked Monthly Ticks

Not the answer you're looking for? Browse other questions tagged or ask your own question.