I am trying to set a space between the boxplots (between the green and orange boxes) created with Python Seaborn
module's sns.boxplot()
. Please see attached the graph, that the green and orange subplot boxes are stuck to each other, making it visually not the most appealing.
Can't find a way to do that, anyone could find a way (code attached)?
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns
tips = sns.load_dataset("tips")
sns.set(style="ticks", palette='Set2', font='Roboto Condensed')
sns.set_context("paper", font_scale=1.1, rc={"lines.linewidth": 1.1})
g=sns.factorplot(x="time", y="total_bill", hue="smoker",
col="day", data=tips, kind="box", size=4, aspect=0.5,
width=0.8,fliersize=2.5,linewidth=1.1, notch=False,orient="v")
sns.despine(trim=True)
g.savefig('test6.png', format='png', dpi=600)
The Seaborn
boxplot documentation is here: http://stanford.edu/~mwaskom/software/seaborn/generated/seaborn.boxplot.html
Running the danger that this is not needed anymore, I found a solution to this problem. When drawing boxplots
directly with matplotlib, the arrangement of the boxes can be controlled with the width
and position
keywords. However, when passing the positions
keyword to sns.factorplot(kind='box',...)
, one gets a
TypeError: boxplot() got multiple values for keyword argument 'positions'
To get around this, one can set the widths of the boxes 'manually' after the boxplot has been created. This is a bit tedious, because the boxes are stored as PatchPatches
within the individual Axes
instances of the FacedGrid
that is returned by sns.factorplot
. Instead of the simple (x,y,width,height)
syntax that Rects
have, PathPatches
use vertices to define the corners, which involves slightly more computation when one wants to adjust the boxes. On top of everything else, the PathPatches
returned by matplotlib.boxplot
contain an extra (ignored) vertex for the Path.CLOSEPOLY
code, which is set to (0,0)
and is best ignored. In addition to the box, the horizontal line that marks the median is now too wide and needs to be adjusted as well.
Below I define a function that adjusts widths of the boxes generated by the OP's example code(note the extra import):
from matplotlib.patches import PathPatch
def adjust_box_widths(g, fac):
"""
Adjust the withs of a seaborn-generated boxplot.
"""
##iterating through Axes instances
for ax in g.axes.flatten():
##iterating through axes artists:
for c in ax.get_children():
##searching for PathPatches
if isinstance(c, PathPatch):
##getting current width of box:
p = c.get_path()
verts = p.vertices
verts_sub = verts[:-1]
xmin = np.min(verts_sub[:,0])
xmax = np.max(verts_sub[:,0])
xmid = 0.5*(xmin+xmax)
xhalf = 0.5*(xmax - xmin)
##setting new width of box
xmin_new = xmid-fac*xhalf
xmax_new = xmid+fac*xhalf
verts_sub[verts_sub[:,0] == xmin,0] = xmin_new
verts_sub[verts_sub[:,0] == xmax,0] = xmax_new
##setting new width of median line
for l in ax.lines:
if np.all(l.get_xdata() == [xmin,xmax]):
l.set_xdata([xmin_new,xmax_new])
calling this function with
adjust_box_widths(g, 0.9)
gives the following output: