FacetGrid axes sharing x axis across rows / y-axis across columns

Question:

FacetGrid from seaborn creates a grid of subpots allowing you to explore conditional relationships in your dataset.

Two of the keyword arguments the function accepts are sharex and sharey, which according to the docs:

share{x,y} : bool, optional

If true, the facets will share y axes across columns and/or x axes across rows.

But I don’t see any other way to control the way facets/subplots share axes. And so here comes the…

Question:

Is there any way to share the x axis across columns and/or the y axes across rows?

I’m trying to obtain density plots of different quantities (in rows) for different conditions (in columns and hue). Thus, I would my subplots to share both the x and y axis across rows, but no linkage across columns.

Asked By: mgab

||

Answers:

The FacetGrid object contains a collection of axes (a numpy array in the shape of the grid). You could manually set axes limits on each axis in the array. That requires a lot of iteration/post-processing, though. Below is a quick example.

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

data = pd.DataFrame({'a':np.random.random(1000), 
                     'b':np.random.random(1000), 
                     'c':np.random.random_integers(0,3,1000),
                     'x':np.random.random_integers(1,5,1000),
                     'y':np.random.random_integers(1,5,1000)})

g = sns.FacetGrid(data=data, col='x', row='y', hue='c', sharex=False, sharey=False)
g.map(plt.scatter, 'a', 'b').add_legend()
for axlist in g.axes:
    for ax in axlist:
        ax.set_ylim(.2,.3)
    break
print(type(g.axes))
plt.show()

However, if you don’t mind skipping seaborn and working directly in matplotlib, you can work with a GridSpec object instead of a FacetGrid, as seen in the example here. Then you can specify specifically which axes are shared.

import pandas as pd
import seaborn as sns
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import gridspec

data = pd.DataFrame({'a':np.random.random(1000), 
                     'b':np.random.random(1000), 
                     'c':np.random.random_integers(0,3,1000),
                     'x':np.random.random_integers(1,5,1000),
                     'y':np.random.random_integers(1,5,1000)})

colors = ['b', 'r', 'g', 'y']

fig = plt.figure(figsize=(6,6))
gs = gridspec.GridSpec(len(data['x'].unique()),len(data['y'].unique()))
g = data.groupby(['x','y'])
for i, (ind, grp) in enumerate(g):
    if i % 5 == 0: 
        ax = fig.add_subplot(gs[i])
    else:
        ax2 = fig.add_subplot(gs[i], sharex = ax)
        plt.setp(ax2.get_yticklabels(), visible=False)

    subgroups = grp.groupby('c')
    for index, sub in subgroups:
        plt.scatter(sub['a'], sub['b'], color=colors[index])


plt.show()

In this example, we share the x-axes across rows (each i % 5 == 0). Since the GridSpec object is just a numpy array of axes, we can initialize them in any convenient order and share axes as we wish. With GridSpec, you have a lot more customization, but a lot of the nice parts of seaborn (hue, etc) have to be coded manually.

Answered By: SNygard

It’s not properly documented, but those parameters are passed straight to plt.subplots, which allows you to set the values for those parameters to "row" or "col" so that axes are only shared within rows or columns, and not across the whole grid.

I’m not sure I understand exactly how you want your plot to look, but I think you can do something like:

tips = sns.load_dataset("tips")
g = sns.FacetGrid(tips, col="smoker", row="time", sharey="col")
g.map(plt.hist, "total_bill")

That said, in general I would try to share axes across the grid (i.e., structure the plot to share y axes across the rows, not across the columns) to ease visual comparisons.

Answered By: mwaskom
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.