pythonmatplotlibseabornseaborn-objects

How can I rotate axis labels in a faceted seaborn.objects plot?


I am working with the excellent seaborn.objects module in the most recent version of seaborn.

I would like to produce a plot:

Rotating x-axis labels is not directly supported within seaborn.objects (and standard stuff like plt.xticks() after making the graph doesn't work), but the documentation suggests doing it using the .on() method. .on() takes a matplotlib figure/subfigure or axis object and builds on top of it. As I pointed out in my answer to this question, the following code works to rotate axis labels:

import seaborn.objects as so
import matplotlib.pyplot as plt
import pandas as pd

df = pd.DataFrame({'a':[1,2,3,4],
                   'b':[5,6,7,8],
                   'c':['a','a','b','b']})

fig, ax = plt.subplots()
ax.xaxis.set_tick_params(rotation=90)

(so.Plot(df, x = 'a', y = 'b')
 .add(so.Line())
 .on(ax))

However, if I add facets by changing the graph code to

(so.Plot(df, x = 'a', y = 'b')
 .add(so.Line())
 .on(ax)
 .facet('c'))

I get the error Cannot create multiple subplots after calling Plot.on with a <class 'matplotlib.axes._axes.Axes'> object. You may want to use a <class 'matplotlib.figure.SubFigure'> instead.

However, if I follow the instruction, and instead use the fig object, rotating the axes in that, I get a strange dual x-axis, one with rotated labels unrelated to the data, and the actual graph's labels, unrotated:

fig, ax = plt.subplots()
plt.xticks(rotation = 90)

(so.Plot(df, x = 'a', y = 'b')
 .add(so.Line())
 .on(fig)
 .facet('c'))

Faceted graph with strange dual labeling

How can I incorporate rotated axis labels with facets?


Solution

  • You're ending up with multiple axes plotted on top of each other. Note the parameter description for Plot.on:

    Passing matplotlib.axes.Axes will add artists without otherwise modifying the figure. Otherwise, subplots will be created within the space of the given matplotlib.figure.Figure or matplotlib.figure.SubFigure.

    Additionally, the pyplot functions (i.e. plt.xticks) only operate on the "current" axes, not all axes in the current figure.

    So, two step solution:

    Example:

    fig = plt.figure()
    
    (so.Plot(df, x = 'a', y = 'b')
     .add(so.Line())
     .on(fig)
     .facet('c')
     .plot()
    )
    
    for ax in fig.axes:
        ax.tick_params("x", rotation=90)
    

    enter image description here

    Note that it will likely become possible to control tick label rotation directly through the Plot API, although rotating tick labels (especially to 90 degrees) is often not the best way to make overlapping labels readable.