Search code examples
pythonseaborn

How to work around the fact that FacetGrid doesn't have a style parameter?


I can't figure out how to vary both hue and style in a seaborn FacetGrid mapped to lineplot, and still end up with a legend. Setup:

import numpy as np
import seaborn as sns
# add variables "group" and "stim_order" to the fMRI dataset, for demo purposes
df = sns.load_dataset('fmri')
df['group'] = df['subject'].map(lambda subj: int(subj.lstrip('s')) % 2)
df['stim_order'] = df['subject'].map({subj: np.random.choice([0, 1])
                                      for subj in df['subject'].unique()})

The following would be ideal, but isn't implemented (FacetGrid has no "style" param):

g = sns.FacetGrid(data=df, row='region', col='event', hue='group', style='stim_order')
g.map(sns.lineplot, 'timepoint', 'signal')

This next approach also doesn't work, because apparently kwargs passed to FacetGrid.map() cannot be references to columns in the data:

g = sns.FacetGrid(data=df, row='region', col='event', hue='group')
g.map(sns.lineplot, 'timepoint', 'signal', style='stim_order')
# fails with ValueError: Could not interpret input 'stim_order'

I can work around these limitations by not specifying "hue" when setting up the FacetGrid, and adding a new column "size" to the data, so that I can pass all the data-related parameters as positional args. But then I get no legend:

df['size'] = 1
g = sns.FacetGrid(data=df, row='region', col='event')
g.map(sns.lineplot, 'timepoint', 'signal', 'group', 'size', 'stim_order')

2x2 seaborn FacetGrid figure

Is there a way to get a plot like this, but that has a brief legend_out containing the hue and style mappings?


Solution

  • You can use g.map_dataframe instead of g.map and get the behaviour you wish.

    Using your code would result in:

    g = sns.FacetGrid(data=df, row='region', col='event', hue='group')
    g.map_dataframe(sns.lineplot, 'timepoint', 'signal', style='stim_order')