Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matplotlib Subplot Datetime X-Axis Ticks Not Working As Intended

I'm attempting to plot many plots, here's a sample of how the data is organized:

dataframe

My intention is to build a series of subplots for either hours or days (say 7 days in a week, or 24 hours in a day) using google analytics data. My index are date-time objects.

Here's an example of how a single plot looks, when the axis is done correctly.

from datetime import datetime, date, timedelta
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import matplotlib.dates as dates

#creating our graph and declaring our locator/formatters used in axis labelling.
hours = dates.HourLocator(interval=2)
hours_ = dates.DateFormatter('%I %p')

el = datetime(year=2016, day=1, month=3, hour=0)
fig, ax = plt.subplots(ncols = 1, nrows= 1)
fig.set_size_inches(18.5, 10.5)
fig.tight_layout()
ax.set_title(el.strftime('%a, %m/%d/%y'))
ax.plot(df_total.loc[el:el+timedelta(hours=23, minutes=59),:].index, 
                             df_total.loc[el:el+timedelta(hours=23, minutes=59),:].hits, '-')
ax.xaxis.set_major_locator(hours)
ax.xaxis.set_major_formatter(hours_) 
fig.show()

right graph!

As you can see, the x axis looks good, working as intended with the right ticks/date labels.

However, when I try and run the same plot on a subplot series, I'm running into the following error. Here's my code:

fig, ax = plt.subplots(ncols = 3, nrows= 2)
fig.set_size_inches(18.5, 10.5)
fig.tight_layout()

nrows=2
ncols=3

count = 0

for row in range(nrows):
    for column in range(ncols):
        el = cleaned_date_range[count]
        ax[row][column].set_title(el.strftime('%a, %m/%d/%y'))
        ax[row][column].xaxis.set_major_locator(hours)
        ax[row][column].xaxis.set_major_formatter(hours_)
        ax[row][column].plot(df_total.loc[el:el+timedelta(hours=23,minutes=59),:].index, df_total.loc[el:el+timedelta(hours=23,minutes=59),:].hits)
        count += 1

        if count == 7:
            break

However, that yields the very funky plot below, with mislabelled axes:

wrong graph!

I experimented with adding an additional row to see if it was just covering up because of vertical space: enter image description here

but was confronted with the same behavior, only the last subplot's axes appears to be working with the rest not working.

Any insight would be appreciated!

like image 898
mburke05 Avatar asked Dec 10 '22 18:12

mburke05


2 Answers

so the answer is in the following github issue raised a few years ago related to the set_major_locator() and set_major_formatter() objects:

https://github.com/matplotlib/matplotlib/issues/1086/

to quote eric:

"You are missing something, but it is something that is quite non-intuitive and easy to miss: Locators can't be shared among axes. The set_major_locator() method assigns its axis to that Locator, overwriting any axis that was previously assigned."

so the solution is to instantiate a new dates.MinuteLocator and dates.DateFormatter object for each new axes, e.g:

for ax in list_of_axes:
    minutes = dates.MinuteLocator(interval=5)
    minutes_ = dates.DateFormatter('%I:%M %p')
    ax.xaxis.set_major_locator(minutes)
    ax.xaxis.set_major_formatter(minutes_)

I've experimented and it looks like you don't need to reference the dates.Locator and dates.Formatter objects after the plot so it's ok to just re-instantiate with each loop using the same name. (I could be wrong here though!)

like image 162
mburke05 Avatar answered May 21 '23 22:05

mburke05


I had the same missing subplot datetime x-axis tick marks issue. The following code, which is quite similar to the OP's, seems to work, see the attached figure. However, I'm using matplotlib 3.1.0, perhaps the issue has been addressed in this version? But I do have one observation: if I enable fig.autofmt_xdate() for the second subplot, the first subplot datetime x-axis will not display.

fig = plt.figure()
plt.rcParams['figure.figsize'] = (width, height)
plt.subplots_adjust(wspace=0.25, hspace=0.2)

ax = fig.add_subplot(2,1,1)
ax.xaxis.set_major_locator(MonthLocator(bymonthday=1))
ax.xaxis.set_major_formatter(DateFormatter('%Y-%b'))
ax.plot(df1['DATE'], df1['Movement'], '-')
plt.ylabel(r'$D$', fontsize=18)
plt.xticks(fontsize=12)
plt.yticks(fontsize=16)
plt.legend(fontsize=16, frameon=False)
fig.autofmt_xdate()

ax = fig.add_subplot(2,1,2)
ax.xaxis.set_major_locator(MonthLocator(bymonthday=1))
ax.xaxis.set_major_formatter(DateFormatter('%Y-%b'))
ax.plot(df2['DATE'], df2['Movement'], '-')
#plt.ylabel(r'$D`enter code here`$', fontsize=18)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.legend(fontsize=16, frameon=False)
#fig.autofmt_xdate()

plt.show()

enter image description here

like image 31
RandomWalker Avatar answered May 21 '23 22:05

RandomWalker