Set space between boxplots in Python Graphs genera

2019-04-08 06:34发布

问题:

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

回答1:

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: