4

I'm building a Seaborn barplot. The x-axis are dates, and the y-axis are integers.

I'd like to format major/minor ticks for the dates. I'd like Mondays' ticks to be bold and a different color (ie, "major ticks"), with the rest of the week less bold.

I have not been able to get major and minor tick formatting on the x-axis to work with Seaborn barplots. I'm stumped, and thus turning here for help.

I'm starting with the stackoverflow example that answered this question: Pandas timeseries plot setting x-axis major and minor ticks and labels

If I do a simple modification it to use a Seaborn barplot and I lose my X-axis ticks:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as dates

import seaborn as sns

idx = pd.date_range('2011-05-01', '2011-07-01')
s = pd.Series(np.random.randn(len(idx)), index=idx)

###########################################
## Swap out these two lines of code:
#fig, ax = plt.subplots()
#ax.plot_date(idx.to_pydatetime(), s, 'v-')

## with this one
ax = sns.barplot(idx.to_pydatetime(), s)
###########################################

ax.xaxis.set_minor_locator(dates.WeekdayLocator(byweekday=(1),
                                                interval=1))
ax.xaxis.set_minor_formatter(dates.DateFormatter('%d\n%a'))
ax.xaxis.grid(True, which="minor")
ax.yaxis.grid()
ax.xaxis.set_major_locator(dates.MonthLocator())
ax.xaxis.set_major_formatter(dates.DateFormatter('\n\n\n%b\n%Y'))
plt.tight_layout()

## save the result to a png instead of plotting to screen:
myFigure = plt.gcf()
myFigure.autofmt_xdate()
myFigure.set_size_inches(11,3.8)

plt.title('Example Chart', loc='center')

plt.savefig('/tmp/chartexample.png', format='png', bbox_inches='tight')

I've tried a variety of approaches but something in Seaborn seems to be overriding or undoing any attempts at major and minor axis formatting that I've managed to cook up yet beyond some simple styling for all ticks when I use set_xticklabels().

I can sort of get formatting on just the major ticks by using MultipleLocator(), but I can't get any formatting on the minor ticks.

I've also experimented with myFigure.autofmt_xdate() to see if it would help, but it doesn't seem to like mixed major & minor ticks on the same axis either.

Community
  • 1
  • 1
rotten
  • 1,580
  • 19
  • 25

1 Answers1

4

I came across this while trying to solve the same problem. Based on the useful pointer from @mwaskom (that categorical plots like boxplots lose their structure and just become date-named categories) and ended up doing the location and formatting in Python as so:

from datetime import datetime
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as dates
import seaborn as sns

idx = pd.date_range('2011-05-01', '2011-07-01')
s = pd.Series(np.random.randn(len(idx)), index=idx)

fig, ax = plt.subplots(figsize = (12,6))
ax = sns.barplot(idx.to_pydatetime(), s, ax = ax)

major_ticks = []
major_tick_labels = []
minor_ticks = []
minor_tick_labels = []

for loc, label in zip(ax.get_xticks(), ax.get_xticklabels()):
  when = datetime.strptime(label.get_text(), '%Y-%m-%d %H:%M:%S')
  if when.day == 1:
    major_ticks.append(loc)
    major_tick_labels.append(when.strftime("\n\n\n%b\n%Y"))
  else:
    minor_ticks.append(loc)
    if when.weekday() == 0:
      minor_tick_labels.append(when.strftime("%d\n%a"))
    else:
      minor_tick_labels.append(when.strftime("%d"))

ax.set_xticks(major_ticks)
ax.set_xticklabels(major_tick_labels)
ax.set_xticks(minor_ticks, minor=True)
ax.set_xticklabels(minor_tick_labels, minor=True)

Of course, you don't have to set the ticks based on parsing the labels which were installed from the data, if it's easier to start with the source data and just keep the indices aligned, but I prefer to have a single source of truth.

You can also mess with font weight, rotation, etc, on individual labels by getting the Text objects for the relevant label and calling set_ methods on it.

Boxplot produced by above code

Tim Dierks
  • 2,168
  • 15
  • 28