Skip to content

Instantly share code, notes, and snippets.

@mdbecker
Created October 24, 2018 15:44
Show Gist options
  • Save mdbecker/c21e6a8a6ce893b61eecd880d9f18a83 to your computer and use it in GitHub Desktop.
Save mdbecker/c21e6a8a6ce893b61eecd880d9f18a83 to your computer and use it in GitHub Desktop.
miniboxplot a.la. seaborn violinplot
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
class _MiniBoxPlotter(sns.categorical._ViolinPlotter):
def draw_violins(self, ax):
"""Draw the violins onto `ax`."""
for i, group_data in enumerate(self.plot_data):
kws = dict(edgecolor=self.gray, linewidth=self.linewidth)
# Option 1: we have a single level of grouping
# --------------------------------------------
if self.plot_hues is None:
support, density = self.support[i], self.density[i]
# Handle special case of no observations in this bin
if support.size == 0:
continue
# Handle special case of a single observation
elif support.size == 1:
val = np.asscalar(support)
d = np.asscalar(density)
self.draw_single_observation(ax, i, val, d)
continue
# Draw the interior representation of the data
if self.inner is None:
continue
# Get a nan-free vector of datapoints
violin_data = sns.utils.remove_na(group_data)
# Draw box and whisker information
self.draw_box_lines(ax, violin_data, support, density, i)
# Option 2: we have nested grouping by a hue variable
# ---------------------------------------------------
else:
offsets = self.hue_offsets
for j, hue_level in enumerate(self.hue_names):
support, density = self.support[i][j], self.density[i][j]
kws["facecolor"] = self.colors[j]
# Add legend data, but just for one set of violins
if not i:
self.add_legend_data(ax, self.colors[j], hue_level)
# Handle the special case where we have no observations
if support.size == 0:
continue
# Handle the special case where we have one observation
elif support.size == 1:
val = np.asscalar(support)
d = np.asscalar(density)
if self.split:
d = d / 2
at_group = i + offsets[j]
self.draw_single_observation(ax, at_group, val, d)
continue
# Option 2a: we are drawing a single split violin
# -----------------------------------------------
if self.split:
if self.inner is None:
continue
# The box and point interior plots are drawn for
# all data at the group level, so we just do that once
if not j:
continue
# Get the whole vector for this group level
violin_data = sns.utils.remove_na(group_data)
# Draw box and whisker information
self.draw_box_lines(ax, violin_data,
support, density, i)
# Option 2b: we are drawing full nested violins
# -----------------------------------------------
else:
# Draw the interior representation
if self.inner is None:
continue
# Get a nan-free vector of datapoints
hue_mask = self.plot_hues[i] == hue_level
violin_data = sns.utils.remove_na(group_data[hue_mask])
# Draw box and whisker information
self.draw_box_lines(ax, violin_data,
support, density,
i + offsets[j])
def miniboxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None,
bw="scott", cut=2, scale="area", scale_hue=True, gridsize=100,
width=.8, inner="box", split=False, dodge=True, orient=None,
linewidth=None, color=None, palette=None, saturation=.75,
ax=None, **kwargs):
plotter = _MiniBoxPlotter(x, y, hue, data, order, hue_order,
bw, cut, scale, scale_hue, gridsize,
width, inner, split, dodge, orient, linewidth,
color, palette, saturation)
if ax is None:
ax = plt.gca()
plotter.plot(ax)
return ax
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment