Is it possible to generate a jointplot with boxplots as marginals instead of a distplot?
From the arguments it obviously does not seem possible and the error is throwing when I force it suggests that as well:
g.plot_marginals(sns.boxplot)
> TypeError: boxplot() got an unexpected keyword argument 'vertical'
Is this a feasible feature request? Or is there any alternative within seaborn to achive something similar?
Anything is possible with the right function signature...
In your case you need to write a wrapper for boxplot that has a similar API as distplot, i.e. (untested):
```python
def marginal_boxplot(a, vertical=False, *kws):
if vertical:
sns.boxplot(y=a, *kws)
else:
sns.boxplot(x=a, **kws)
````
PS this kind of question is typically better suited for StackOveflow.
This is very nice to know, thanks. I got this:

I think the next problem that I have is that sns.displot does not have hue support, and I would like to have a sns.boxplot with a hue as on the sns.regplot.
P.S: I can gladly open a StackOverflow ticket question
I solved my problem by hacking the marginal_boxplot function and defining there the data.frame and the variables to plot.
It's definitely not portable to other situations but in case it is useful for anyone else I leave it here the code and the outcome plot.
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
plot_df = pd.read_csv('jointplot_boxplot_marginal.csv')
pal = {1: '#eb2525', 0: '#888888'}
def marginal_boxplot(a, vertical=False, **kws):
g = sns.boxplot(x='var_z', y='var_y', orient='v', **kws) if vertical else sns.boxplot(x='var_x', y='var_z', orient='h', **kws)
g.set_ylabel('')
g.set_xlabel('')
sns.set(rc={'axes.linewidth': .3, 'xtick.major.width': .3, 'ytick.major.width': .3, 'xtick.major.size': 2.5, 'ytick.major.size': 2.5}, style='ticks', context='paper', font_scale=.75)
g = sns.jointplot(
'var_x', 'var_y', plot_df, 'reg', color=pal[0], space=0, truncate=True, ratio=8,
marginal_kws={
'hist': False, 'rug': False, 'kde': False,
},
annot_kws={
'template': 'Pearson={val:.2g}, p={p:.1e}', 'loc': 4
},
joint_kws={
'scatter_kws': {'s': 20, 'edgecolor': 'w', 'linewidth': .5, 'alpha': .8},
'line_kws': {'linewidth': .5}
}
)
g.plot_marginals(marginal_boxplot, palette=pal, data=plot_df, linewidth=.3, fliersize=1, notch=True)
g.x = plot_df.loc[plot_df['var_z'] == 1, 'var_x']
g.y = plot_df.loc[plot_df['var_z'] == 1, 'var_y']
g.plot_joint(sns.regplot, color=pal[1], truncate=True, fit_reg=False, scatter_kws={'s': 20, 'edgecolor': 'w', 'linewidth': .5, 'alpha': .8})
g.ax_joint.axhline(0, ls='-', lw=0.3, c='black', alpha=.2)
g.ax_joint.axvline(0, ls='-', lw=0.3, c='black', alpha=.2)
plt.gcf().set_size_inches(2, 2)
plt.savefig('jointplot_boxplot_marginal.png', bbox_inches='tight', dpi=600)
plt.close('all')

Nicely done. The code could be a little simpler in that you don't really need to re-define the variables on the JointGrid and use g.plot_joint to draw the red scatterplots; it should be sufficient to just call
sns.regplot(x="var_x", y="var_y", data=plot_df.query("var_z == 1"), ..., ax=g.ax_joint)
I'm not sure that mocking what jointplot is doing internally is buying you anything there.
I guess also while I'm nitpicking you would save some effort (i.e. disabling the jointplot default distplot kwargs) by simply using JointGrid directly instead of working around the (in)conveniences of jointplot.
The reason why I forced the variables like that was because if I redefine them like this:
g.plot_joint(sns.regplot, x="var_x", y="var_y", data=plot_df.query("var_z == 1"), color=pal[1], truncate=True, fit_reg=False, scatter_kws={'s': 20, 'edgecolor': 'w', 'linewidth': .5, 'alpha': .8})
I get: TypeError: regplot() got multiple values for argument 'x'
But I have the impression you were suggesting something different.
Thanks for the tips.
But I have the impression you were suggesting something different.
Yes, I'm saying that once the JointGrid is created, you don't have to use its methods to plot things on it. It has an attribute called ax_joint that is simply the underlying matplotlib axes and can be passed to any axes-level seaborn function (or any of its plotting methods can be called if you want to use matplotlib directly).
Very useful simplified the code a lot, thanks. Let me know of any other tips :)
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.stats import pearsonr
# Data
plot_df = pd.read_csv("jointplot_boxplot_marginal.csv")
# Palette
pal = {1: "#eb2525", 0: "#888888"}
# Marginal boxplot function
def marginal_boxplot(a, vertical=False, **kws):
g = sns.boxplot(x="var_z", y="var_y", orient="v", **kws) if vertical else sns.boxplot(x="var_x", y="var_z", orient="h", **kws)
g.set_ylabel("")
g.set_xlabel("")
sns.set(rc={"axes.linewidth": .3, "xtick.major.width": .3, "ytick.major.width": .3, "xtick.major.size": 2.5, "ytick.major.size": 2.5}, style="ticks", context="paper", font_scale=.75)
g = sns.JointGrid("var_x", "var_y", plot_df, space=0, ratio=8)
g.plot_marginals(marginal_boxplot, palette=pal, data=plot_df, linewidth=.3, fliersize=1, notch=True)
sns.regplot(x="var_x", y="var_y", data=plot_df, color=pal[0], truncate=True, fit_reg=True, scatter_kws={"s": 20, "edgecolor": "w", "linewidth": .5, "alpha": .8}, line_kws={"linewidth": .5}, ax=g.ax_joint)
sns.regplot(x="var_x", y="var_y", data=plot_df.query("var_z == 1"), color=pal[1], truncate=True, fit_reg=False, scatter_kws={"s": 20, "edgecolor": "w", "linewidth": .5, "alpha": .8}, ax=g.ax_joint)
g.annotate(pearsonr, template="Pearson={val:.2g}, p={p:.1e}", loc=4)
g.ax_joint.axhline(0, ls="-", lw=0.3, c="black", alpha=.2)
g.ax_joint.axvline(0, ls="-", lw=0.3, c="black", alpha=.2)
plt.gcf().set_size_inches(2, 2)
plt.savefig("jointplot_boxplot_marginal.png", bbox_inches="tight", dpi=600)
plt.close("all")
The only other comment I'd make is that you should name the return value of sns.boxplot ax not g. By convention the former is used when functions return matplotlib Axes and the latter is used when the function returns a seaborn Grid object. While it's purely a matter of coding convention and won't affect execution, I think it will help both you keep in mind the different behaviors of the functions and will make it more clear to people who are reading your code what kind of objects are being used.
Otherwise, nice plot.
Most helpful comment
I solved my problem by hacking the
marginal_boxplotfunction and defining there the data.frame and the variables to plot.It's definitely not portable to other situations but in case it is useful for anyone else I leave it here the code and the outcome plot.