How to add additional plots to a seaborn FacetGrid and specify colors
Asked Answered
D

3

6

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

Dannielledannon answered 19/5, 2021 at 22:20 Comment(0)
D
10

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

Durand answered 20/5, 2021 at 0:12 Comment(0)
P
3
  • 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

Procuration answered 19/5, 2021 at 23:23 Comment(0)
C
0

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

Cytoplast answered 21/10, 2021 at 17:16 Comment(4)
This doesn't generalise easily.Ottillia
@Ottillia can you elaborate?Cytoplast
You need to know before hand how many lines there will be and what sim values they correspond to to implement this. It would presumably be possible to generalise by getting a list of unique sim values and then having the hueColor set to a list of equal length, all equal "r", except the final one. Explicitly stating this I think would make the answer more applicable.Ottillia
@climatestudent, yes to generalize, you simply get a number of elements(sim) for the graph. Then make all but one the same color, its what I have been doing. Good point. Maybe I will edit the answerCytoplast

© 2022 - 2025 — McMap. All rights reserved.