1

Currently I am charting data from some historical point to a point in current time. For example, January 2019 to TODAY (February 2021). However, my matplotlib chart only shows dates from January 2019 to January 2021 on the x-axis (with the last February tick missing) even though the data is charted to today's date on the actual plot.

Is there any way to ensure that the first and last month is always reflected on the x-axis chart? In other words, I would like the x-axis to have the range displayed (inclusive).

Picture of x axis (missing February 2021)

enter image description here The data charted here is from January 2019 to TODAY (February 12th).

Here is my code for the date format:

fig.autofmt_xdate()
date_format = mdates.DateFormatter("%b-%y")
ax.xaxis.set_major_formatter(date_format)

EDIT: The numbers after each month represent years.

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
coffee_cup
  • 11
  • 1
  • 3
  • It is difficult to establish what your desired output is. Even a monthly tick would not guarantee that all months appear as ticks (think range 29 Mar 2018 to 2 Mar 2020 - matplotlib has to choose a day of the month where to place the tick, so either the end or the start month will not be labeled). You could set a tick at the first and last data point and define the number of ticks in between. That would make the graph more difficult to read. You could add manually the last month, but this would make the graph look irregular. So, how do you imagine your final graph labeling? – Mr. T Feb 12 '21 at 11:28
  • Does my answer provide the solution you were looking for? – Patrick FitzGerald Feb 21 '21 at 11:10

2 Answers2

2

I am not aware of any way to do this other than by creating the ticks from scratch.

In the following example, a list of all first-DatetimeIndex-timestamp-of-the-month is created from the DatetimeIndex of a pandas dataframe, starting from the month of the first date (25th of Jan.) up to the start of the last ongoing month. An appropriate number of ticks is automatically selected by the step variable and the last month is appended and then removed with np.unique when it is a duplicate. The labels are formatted from the tick timestamps.

This solution works for any frequency smaller than yearly:

import numpy as np                 # v 1.19.2
import pandas as pd                # v 1.1.3
import matplotlib.pyplot as plt    # v 3.3.2

# Create sample dataset
start_date = '2019-01-25'
end_date = '2021-02-12'
rng = np.random.default_rng(seed=123) # random number generator
dti = pd.date_range(start_date, end_date, freq='D')
variable = 100 + rng.normal(size=dti.size).cumsum()
df = pd.DataFrame(dict(variable=variable), index=dti)

# Create matplotlib plot
fig, ax = plt.subplots(figsize=(10, 2))
ax.plot(df.index, df.variable)

# Create list of monthly ticks made of timestamp objects
monthly_ticks = [timestamp for idx, timestamp in enumerate(df.index)
                 if (timestamp.month != df.index[idx-1].month) | (idx == 0)]

# Select appropriate number of ticks and include last month
step = 1
while len(monthly_ticks[::step]) > 10:
    step += 1
ticks = np.unique(np.append(monthly_ticks[::step], monthly_ticks[-1]))

# Create tick labels from tick timestamps
labels = [timestamp.strftime('%b\n%Y') if timestamp.year != ticks[idx-1].year
          else timestamp.strftime('%b') for idx, timestamp in enumerate(ticks)]

plt.xticks(ticks, labels, rotation=0, ha='center');

first_last_months

As you can see, the first and last months are located at an irregular distance from the neighboring tick.


In case you are plotting a time series with a discontinous date range (e.g. weekend and holidays not included) and you are not using the DatetimeIndex for the x-axis (like this for example: ax.plot(range(df.index.size), df.variable)) so as to avoid gaps with straight lines showing up on short time series and/or very wide plots, then replace the last line of code with this:

plt.xticks([df.index.get_loc(tick) for tick in ticks], labels, rotation=0, ha='center');
Patrick FitzGerald
  • 3,280
  • 2
  • 18
  • 30
0

Matplotlib uses a limited number of ticks. It just happens that for February 2021 no tick is used. There are two things you could try. First try setting the axis limits to past today with:

ax.set_xlim(start_date, end_date)

What you could also try, is using even more ticks:

ax.set_xticks(np.arange(np.min(x), np.max(x), n_ticks))

Where n_ticks stands for the amount of ticks and x for the values on the x-axis.

Jan Willem
  • 820
  • 1
  • 6
  • 24
  • It doesn't stand for February 22nd, but February 2021. And today is already February 2021 (I have data up to today). – coffee_cup Feb 12 '21 at 10:51
  • Exactly, but in your current tick formatting the next tick would be 21 or 22 February, of which you do not have data yet. – Jan Willem Feb 12 '21 at 11:02
  • Why? I have data from January 1st 2019 to February 12th, 2021. What does the February 21st or 22nd 2021 have to do with anything? Thanks. Just trying to understand. – coffee_cup Feb 12 '21 at 11:18
  • Matplotlib is just placing ticks every now and then. If you add extra ticks February will also show up. I misunderstood the date on your axis, so the exact day in February does not matter, but that does not change the fix. If you want to see February, you should more ticks. – Jan Willem Feb 12 '21 at 12:32