Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 661
mgab Avatar asked Mar 15 '16 16:03

mgab


People also ask

What does FacetGrid do in Seaborn?

FacetGrid object takes a dataframe as input and the names of the variables that will form the row, column, or hue dimensions of the grid. The variables should be categorical and the data at each level of the variable will be used for a facet along that axis.

Which plots can be plotted using faceted grids?

The plots it produces are often called “lattice”, “trellis”, or “small-multiple” graphics. It can also represent levels of a third variable with the hue parameter, which plots different subsets of data in different colors.

How do I get rid of the y axis in Seaborn?

To remove X or Y labels from a Seaborn heatmap, we can use yticklabel=False.


2 Answers

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.

like image 83
mwaskom Avatar answered Sep 28 '22 01:09

mwaskom


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.

like image 24
SNygard Avatar answered Sep 28 '22 01:09

SNygard