I'm attempting to plot many plots, here's a sample of how the data is organized:
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()
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:
I experimented with adding an additional row to see if it was just covering up because of vertical space:
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!
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!)
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With