how to modify seaborn violin plot legend - python

I have created a simple violin plot from a bands DataFrame (df10 below) using seaborn:
fig, ax = plt.subplots(figsize=(10,4))
ax = sns.violinplot(x='z', y='z_fit', hue='new_col', data=df10, cut=0, palette='Blues', linewidth=1)
ax.set_xlabel('z_sim')
ax.legend()
The legend is plotted automatically with the values of the hue parameter. Using ax.legend() I can only hide the name of the used column ('new_col').
However, I was wondering if there is some way to manually modify the legend (texts, colors and shapes) plotted below:

Example:
import seaborn as sns
tips = sns.load_dataset("tips")
g = sns.FacetGrid(tips, col="time", size=4, aspect=.75)
g = g.map(sns.violinplot, "sex", "total_bill", "smoker", palette={"No": "b", "Yes": "w"}, inner=None, linewidth=1, scale="area", split=True, width=0.75).despine(left=True)
g.fig.get_axes()[0].legend(title= 'smoker',loc='top left',labels=["YES","NO"],edgecolor='red',facecolor='blue',ncol=2)
g.set_axis_labels('lunch','total bill')
For more info run:
help(g.fig.get_axes()[0].legend)

Related

How to share facetgrid x and y axis using seaborn

Running this below code produces seaborn facetgrid graphs.
merged1=merged[merged['TEST'].isin(['VL'])]
merged2=merged[merged['TEST'].isin(['CD4'])]
g = sns.relplot(data=merged1, x='Days Post-ART', y='Log of VL and CD4', col='PATIENT ID',col_wrap=4, kind="line", height=4, aspect=1.5,
color='b', facet_kws={'sharey':True,'sharex':True})
for patid, ax in g.axes_dict.items(): # axes_dict is new in seaborn 0.11.2
ax1 = ax.twinx()
sns.lineplot(data=merged2[merged2['PATIENT ID'] == patid], x='Days Post-ART', y='Log of VL and CD4', color='r')
I've used the facet_kws={'sharey':True, 'sharex':True} to share the x-axis and y-axis but it's not working properly. Can someone assist?
As stated in the comments, the FacetGrid axes are shared by default. However, the twinx axes are not. Also, the call to twinx seems to reset the default hiding of the y tick labels.
You can manually share the twinx axes, and remove the unwanted tick labels.
Here is some example code using the iris dataset:
from matplotlib import pyplot as plt
import seaborn as sns
import numpy as np
iris = sns.load_dataset('iris')
g = sns.relplot(data=iris, x='petal_length', y='petal_width', col='species', col_wrap=2, kind="line",
height=4, aspect=1.5, color='b')
last_axes = np.append(g.axes.flat[g._col_wrap - 1::g._col_wrap], g.axes.flat[-1])
shared_right_y = None
for species, ax in g.axes_dict.items():
ax1 = ax.twinx()
if shared_right_y is None:
shared_right_y = ax1
else:
shared_right_y.get_shared_y_axes().join(shared_right_y, ax1)
sns.lineplot(data=iris[iris['species'] == species], x='petal_length', y='sepal_length', color='r', ax=ax1)
if not ax in last_axes: # remove tick labels from secondary axis
ax1.yaxis.set_tick_params(labelleft=False, labelright=False)
ax1.set_ylabel('')
if not ax in g._left_axes: # remove tick labels from primary axis
ax.yaxis.set_tick_params(labelleft=False, labelright=False)
plt.tight_layout()
plt.show()

Seaborn jointplot color histogram

I'd like to color my histogram according to my palette. Here's the code I used to make this, and here's the error I received when I tried an answer I found on here.
g = sns.jointplot(data=emb_df, x='f0', y='y', kind="hist", hue='klabels', palette='tab10', marginal_kws={'hist_kws': {'palette': 'tab10'}})
plt.show()
UserWarning: The marginal plotting function has changed to `histplot`, which does not accept the following argument(s): hist_kws.
I have also tried this:
plt.setp(g.ax_marg_y.patches, color='grey')
But this does not color my histogram according my 'klabels' parameter, just a flat grey.
The marginal plot is colored by default using the same palette with corresponding hue. So, you could just run it without marginal_kws=. The marginal_kws= go directly to the histplot; instead of marginal_kws={'hist_kws': {'palette': 'tab10'}}, the correct use would be marginal_kws={'palette': 'tab10'}. If you would like stacked bars, you could try marginal_kws={'multiple': 'stack'})
If you want the marginal plots to be larger, the ratio= parameter can be altered. The default is 5, meaning the central plot is 5 times as large as the marginal plots.
Here is an example:
from matplotlib import pyplot as plt
import seaborn as sns
iris = sns.load_dataset('iris')
g = sns.jointplot(data=iris, x='petal_length', y='sepal_length', kind="hist", hue='species', palette='tab10',
ratio=2, marginal_kws={'multiple': 'stack'})
sns.move_legend(g.ax_joint, loc='upper left') # optionally move the legend; seaborn >= 0.11.2 needed
plt.show()
To have these plots side-by-side as subplots, you can call the underlying sns.histplot either with both x= and y= filled in (2D histogram), only x= given (horizontal histogram) or only y= given (vertical histogram).
from matplotlib import pyplot as plt
import seaborn as sns
iris = sns.load_dataset('iris')
fig, (ax1, ax2, ax3) = plt.subplots(ncols=3, figsize=(15, 4))
sns.histplot(data=iris, x='petal_length', y='sepal_length', hue='species', palette='tab10', legend=False, ax=ax1)
sns.histplot(data=iris, x='petal_length', hue='species', palette='tab10', multiple='stack', legend=False, ax=ax2)
sns.histplot(data=iris, y='sepal_length', hue='species', palette='tab10', multiple='stack', ax=ax3)
sns.move_legend(ax3, bbox_to_anchor=[1.01, 1.01], loc='upper left')
plt.tight_layout()
plt.show()

How to overlay data points on a barplot with a categorical axis

Goal: I am trying to show individual data points in a figure with multiple grouped bar charts using Seaborn.
Problem: I tried to do it with a catplot for the bar chart and another catplot for the individual data points. However, this generates 2 figures: One figure with the bar chart and the other with the individual data points.
Question: Is there a way to show the individual data points in the same figure together with the bar chart using Seaborn?
This is my code generating 2 separate figures:
import seaborn as sns
tips = sns.load_dataset("tips")
g = sns.catplot(
x="sex",
y="total_bill",
hue="smoker",
row="time",
data=tips,
kind="bar",
ci = "sd",
edgecolor="black",
errcolor="black",
errwidth=1.5,
capsize = 0.1,
height=4,
aspect=.7,
)
g = sns.catplot(
x="sex",
y="total_bill",
hue="smoker",
row="time",
data=tips,
kind="strip",
height=4,
aspect=.7,
)
Output:
Question: Is there a way to show the individual data points in the same figure together with the bar chart using Seaborn?
seaborn.catplot is a figure-level plot, and they can't be combined.
As shown below, axes-level plots like seaborn.barplot and seaborn.stripplot can be plotted to the same axes.
import seaborn as sns
tips = sns.load_dataset("tips")
ax = sns.barplot(
x="sex",
y="total_bill",
hue="smoker",
data=tips,
ci="sd",
edgecolor="black",
errcolor="black",
errwidth=1.5,
capsize = 0.1,
alpha=0.5
)
sns.stripplot(
x="sex",
y="total_bill",
hue="smoker",
data=tips, dodge=True, alpha=0.6, ax=ax
)
# remove extra legend handles
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles[2:], labels[2:], title='Smoker', bbox_to_anchor=(1, 1.02), loc='upper left')
Figure-level plots (seaborn.catplot) may not be combined, however, it's possible to map an axes-level plot (seaborn.stripplot) onto a figure-level plot.
See Building structured multi-plot grids
This can be a temperamental process, and may only work when the same columns from the dataframe are being used in the mapped plot.
Tested in python 3.8.11, matplotlib 3.4.3, seaborn 0.11.2
import seaborn as sns
tips = sns.load_dataset("tips")
g = sns.catplot(
x="sex",
y="total_bill",
hue="smoker",
row="time",
data=tips,
kind="bar",
ci = "sd",
edgecolor="black",
errcolor="black",
errwidth=1.5,
capsize = 0.1,
height=4,
aspect=.7,
alpha=0.5)
# map data to stripplot
g.map(sns.stripplot, 'sex', 'total_bill', 'smoker', hue_order=['Yes', 'No'], order=['Male', 'Female'],
palette=sns.color_palette(), dodge=True, alpha=0.6, ec='k', linewidth=1)

Bar labels in matplotlib/Seaborn

In version 3.4, matplotlib added automatic Bar labels:
https://matplotlib.org/stable/users/whats_new.html#new-automatic-labeling-for-bar-charts
I'm trying to use this on a bar plot generated by Seaborn.
fig, axs = plt.subplots(
nrows=2,
)
for i, col in enumerate(['col_1', 'col_2']):
ax = axs[i]
sns.barplot(
x="class",
y=col,
hue="hue_col",
data=data_df,
edgecolor=".3",
linewidth=0.5,
ax=ax
)
ax.bar_label(ax.containers[i]) # Doesn't work
What do I need to do to make this work? example plot
You can loop through the containers and call ax.bar_label(...) for each of them. Note that seaborn creates one set of bars for each hue value.
The following example uses the titanic dataset and sets ci=None to avoid the error bars overlapping with the text (if error bars are needed, one could set a lighter color, e.g. errcolor='gold').
import seaborn as sns
import matplotlib.pyplot as plt
titanic = sns.load_dataset('titanic')
fig, axs = plt.subplots(ncols=2, figsize=(12, 4))
for ax, col in zip(axs, ['age', 'fare']):
sns.barplot(
x='sex',
y=col,
hue="class",
data=titanic,
edgecolor=".3",
linewidth=0.5,
ci=None,
ax=ax
)
ax.set_title('mean ' + col)
ax.margins(y=0.1) # make room for the labels
for bars in ax.containers:
ax.bar_label(bars, fmt='%.1f')
plt.tight_layout()
plt.show()

Combine (overlay) two factorplots in matplotlib

I need to add swarmplot to boxplot in matplotlib, but I don't know how to do it with factorplot. I think I can iterate with subplots, but I would like to learn how to do it with seaborn and factorplot.
A simple example (plotting by using the same axis ax):
import seaborn as sns
tips = sns.load_dataset("tips")
ax = sns.boxplot(x="tip", y="day", data=tips, whis=np.inf)
ax = sns.swarmplot(x="tip", y="day", data=tips, color=".2")
The result:
In my case, I need to overlay the swarm factorplot:
g = sns.factorplot(x="sex", y="total_bill",
hue="smoker", col="time",
data=tips, kind="swarm",
size=4, aspect=.7);
and boxplot
I can't figure out how to use axes (extract from g)?
Something like:
g = sns.factorplot(x="sex", y="total_bill",
hue="smoker", col="time",
data=tips, kind="box",
size=4, aspect=.7);
I want something like this, but with factorplot and boxplot instead of violinplot
Instead of trying to overlay the two subplots of a factorplot with individual boxplots (which is possible, but I don't like it), one can just create the two subplots individually.
You would then loop over the groups and axes an plot a pair of box- and swarmplot to each.
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
tips = sns.load_dataset("tips")
fig, axes = plt.subplots(ncols=2, sharex=True, sharey=True)
for ax, (n,grp) in zip(axes, tips.groupby("time")):
sns.boxplot(x="sex", y="total_bill", data=grp, whis=np.inf, ax=ax)
sns.swarmplot(x="sex", y="total_bill", hue="smoker", data=grp,
palette=["crimson","indigo"], ax=ax)
ax.set_title(n)
axes[-1].get_legend().remove()
plt.show()

Categories