How to add additional plots to a seaborn FacetGrid and specify colors

Question:

Is there a way to create a Seaborn line plot with all the lines gray and the mean as a red line? I’m trying to do this with relplot but I don’t know how to separate the mean from the data (and it appears the mean isn’t being plotted?).

Make reproducible data frame

np.random.seed(1)
n1 = 100
n2 = 10
idx = np.arange(0,n1*2)
x, y, cat, id2 = [], [], [], []

x1 = list(np.random.uniform(-10,10,n2))
for i in idx: 
    x.extend(x1)
    y.extend(list(np.random.normal(loc=0, scale=0.5, size=n2)))
    cat.extend(['A', 'B'][i > n1])
    id2.append(idx[i])

id2 = id2 * n2
id2.sort()
df1 = pd.DataFrame(list(zip(id2, x, y, cat)), 
                  columns =['id2', 'x', 'y', 'cat']
                 )

Plotting attempt

g = sns.relplot(
    data=df1, x='x', y='y', hue='id2',
    col='cat', kind='line',
    palette='Greys',
    facet_kws=dict(sharey=False, 
                   sharex=False
                  ),
    legend=False
)

enter image description here

Asked By: a11

||

Answers:

  • It depends on the desired result. The seaborn.relplot documentation has an example for the fmri dataset that only shows the mean and the ci, so the result depends on how you set the hue and event parameters.
  • To specify a single color for all the lines, use units instead of hue or style (as pointed out by mwaskom), and then set color='grey'.
  • For the requirements of this OP, the accepted answer is the best option. However, in cases which require adding data from a source that isn’t the data used to create the relplot, this solution may be more appropriate, as it allows for accessing each axes of the figure, and adding something from a different data source.
import seaborn as sns
import pandas as pd

# load and select data only where event is stim
fm = sns.load_dataset('fmri').query("event == 'stim'")

# groupby to get the mean for each region by timepoint
fmg = fm.groupby(['region', 'timepoint'], as_index=False).signal.mean()

# plot the fm dataframe
g = sns.relplot(data=fm, col='region', x='timepoint', y='signal',
                units='subject', kind='line', ci=None, color='grey', estimator=None)

# extract and flatten the axes from the figure
axes = g.axes.flatten()

# iterate through each axes
for ax in axes:
    # extract the region
    reg = ax.get_title().split(' = ')[1]
    
    # select the data for the region
    data = fmg[fmg.region.eq(reg)]
    
    # plot the mean line
    sns.lineplot(data=data, x='timepoint', y='signal', ax=ax, color='red', label='mean', lw=3)
    
# fix the legends
axes[0].legend().remove()
axes[1].legend(title='Subjects', bbox_to_anchor=(1, 1), loc='upper left')

enter image description here

Resources

Answered By: Trenton McKinney

I think you want units in the call to relplot and then add a layer of lineplot using map:

import seaborn as sns
import pandas as pd

fm = sns.load_dataset('fmri').query("event == 'stim'")
g = sns.relplot(
    data=fm, kind='line',
    col='region', x='timepoint', y='signal', units='subject',
    estimator=None, color='.7'
)
g.data = fm  # Hack needed to work around bug on v0.11, fixed in v0.12.dev
g.map(sns.lineplot, 'timepoint', 'signal', color='r', ci=None, lw=3)

enter image description here

Answered By: mwaskom

I came here from Seaborn's relplot: Is there a way to assign a specific color to all lines and another color to another single line when using the hue argument? and can’t post there.

But I find the simplest solution is to simply pass a hue_order as well as a palette argument to the relplot call. See below:

import seaborn as sbn
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt


sim = ['a'] * 100 + ['b'] * 100 + ['c'] * 100
var = (['u'] * 50 + ['v'] * 50)*3

x = np.linspace(0, 50, 50)
x = np.hstack([x]*6)

y = np.random.rand(300)

df = pd.DataFrame({'x':x, 'y':y, 'sim':sim, 'var':var})

hueOrder = ["a", "b", "c"] # Specifies the order for the palette
hueColor = ["r", "r", "b"] # Specifies the colors for the hueOrder (just make two the same color, and one different)

sbn.relplot(data=df, x='x', y='y', kind='line', col='var', hue='sim', hue_order=hueOrder, palette=hueColor)
plt.show()

This is a bit different then the accepted answer. But again, I came here from another question that is closed. This also doesn’t use any for loops and should be more straight forward.

Output:
enter image description here

Answered By: Seth