def _scipy_bivariate_kde(x, y, bw, gridsize, cut, clip): """Compute a bivariate kde using scipy.""" data = np.c_[x, y] kde = stats.gaussian_kde(data.T) if isinstance(bw, str): bw = "scotts" if bw == "scott" else bw bw = getattr(kde, "%s_factor" % bw)() x_support = _kde_support(data[:, 0], bw, gridsize, cut, clip[0]) y_support = _kde_support(data[:, 1], bw, gridsize, cut, clip[1]) xx, yy = np.meshgrid(x_support, y_support) z = kde([xx.ravel(), yy.ravel()]).reshape(xx.shape) return xx, yy, z
def _statsmodels_bivariate_kde(x, y, bw, gridsize, cut, clip): """Compute a bivariate kde using statsmodels.""" from statsmodels import nonparametric if isinstance(bw, str): bw_func = getattr(nonparametric.bandwidths, "bw_" + bw) x_bw = bw_func(x) y_bw = bw_func(y) bw = [x_bw, y_bw] elif np.isscalar(bw): bw = [bw, bw] kde = nonparametric.kernel_density.KDEMultivariate([x, y], "cc", bw) x_support = _kde_support(x, kde.bw[0], gridsize, cut, clip[0]) y_support = _kde_support(y, kde.bw[1], gridsize, cut, clip[1]) xx, yy = np.meshgrid(x_support, y_support) z = kde.pdf([xx.ravel(), yy.ravel()]).reshape(xx.shape) return xx, yy, z
def _scipy_univariate_kde(data, bw, gridsize, cut, clip): """Compute a univariate kernel density estimate using scipy.""" kde = stats.gaussian_kde(data, bw_method=bw) if isinstance(bw, str): bw = "scotts" if bw == "scott" else bw bw = getattr(kde, "%s_factor" % bw)() grid = _kde_support(data, bw, gridsize, cut, clip) y = kde(grid) return grid, y
def _bivariate_kde(x, y, filled, kernel, bw, gridsize, cut, clip, axlabel, ax, **kwargs): """Plot a joint KDE estimate as a bivariate contour plot.""" # Determine the clipping if clip is None: clip = [(-np.inf, np.inf), (-np.inf, np.inf)] elif np.ndim(clip) == 1: clip = [clip, clip] # Calculate the KDE if isinstance(bw, str): bw_func = getattr(sm.nonparametric.bandwidths, "bw_" + bw) x_bw = bw_func(x) y_bw = bw_func(y) bw = [x_bw, y_bw] elif np.isscalar(bw): bw = [bw, bw] kde = sm.nonparametric.KDEMultivariate([x, y], "cc", bw) x_support = _kde_support(x, kde.bw[0], gridsize, cut, clip[0]) y_support = _kde_support(y, kde.bw[1], gridsize, cut, clip[1]) xx, yy = np.meshgrid(x_support, y_support) z = kde.pdf([xx.ravel(), yy.ravel()]).reshape(xx.shape) # Plot the contours n_levels = kwargs.pop("n_levels", 10) cmap = kwargs.pop("cmap", "BuGn" if filled else "BuGn_d") if isinstance(cmap, str): if cmap.endswith("_d"): pal = ["#333333"] pal.extend(color_palette(cmap.replace("_d", "_r"), 2)) cmap = blend_palette(pal, as_cmap=True) contour_func = ax.contourf if filled else ax.contour contour_func(xx, yy, z, n_levels, cmap=cmap, **kwargs) # Label the axes if hasattr(x, "name") and axlabel: ax.set_xlabel(x.name) if hasattr(y, "name") and axlabel: ax.set_ylabel(y.name) return ax
def kdeplot(a, npts=1000, shade=False, support_thresh=1e-4, support_min=-np.inf, support_max=np.inf, vertical=False, ax=None, **kwargs): """Calculate and plot kernel density estimate. Parameters ---------- a : ndarray input data npts : int, optional number of x points shade : bool, optional whether to shade under kde curve support_thresh : float, default 1e-4 draw density for values up to support_thresh * max(density) support_{min, max}: float, default to (-) inf if given, do not draw above or below these values (does not affect the actual estimation) vertical : bool, defualt False if True, density is on x-axis ax : matplotlib axis, optional axis to plot on, otherwise creates new one kwargs : other keyword arguments for plot() Returns ------- ax : matplotlib axis axis with plot """ if ax is None: ax = plt.gca() a = np.asarray(a) kde = stats.gaussian_kde(a.astype(float).ravel()) x = _kde_support(a, kde, npts, support_thresh) x = x[x >= support_min] x = x[x <= support_max] y = kde(x) if vertical: y, x = x, y line, = ax.plot(x, y, **kwargs) color = line.get_color() line.remove() kwargs.pop("color", None) ax.plot(x, y, color=color, **kwargs) if shade: ax.fill_between(x, 0, y, color=color, alpha=0.25) return ax
def hacked_statsmodels_bivariate_kde(x, y, bw, gridsize, cut, clip): """Compute a bivariate kde using statsmodels. Modified to give the exponentiated values on the y axis""" import statsmodels.nonparametric.api as smnp from seaborn.utils import _kde_support if isinstance(bw, str): bw_func = getattr(smnp.bandwidths, "bw_" + bw) x_bw = bw_func(x) y_bw = bw_func(y) bw = [x_bw, y_bw] elif np.isscalar(bw): bw = [bw, bw] if isinstance(x, pd.Series): x = x.values if isinstance(y, pd.Series): y = y.values kde = smnp.KDEMultivariate([x, y], "cc", bw) x_support = _kde_support(x, kde.bw[0], gridsize, cut, clip[0]) y_support = _kde_support(y, kde.bw[1], gridsize, cut, clip[1]) xx, yy = np.meshgrid(x_support, y_support) z = kde.pdf([xx.ravel(), yy.ravel()]).reshape(xx.shape) # exponentiate y values return xx, np.exp(yy), z
def distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, hist_kws=None, kde_kws=None, rug_kws=None, fit_kws=None, color=None, vertical=False, axlabel=None, ax=None): """Flexibly plot a distribution of observations. Parameter a : (squeezable to) 1d array Observed data. bins : argument for matplotlib hist(), or None Specification of hist bins, or None to use Freedman-Diaconis rule. hist : bool, default True Whether to plot a (normed) histogram. kde : bool, default True Whether to plot a gaussian kernel density estimate. rug : bool, default False Whether to draw a rugplot on the support axis. fit : random variable object An object with `fit` method, returning a tuple that can be passed to a `pdf` method a positional arguments following an grid of values to evaluate the pdf on. {hist, kde, rug, fit}_kws : dictionaries Keyword arguments for underlying plotting functions. color : matplotlib color, optional Color to plot everything but the fitted curve in. vertical : bool, default False If True, oberved values are on y-axis. axlabel : string, False, or None Name for the support axis label. If None, will try to get it from a.namel if False, do not set a label. ax : matplotlib axis, optional if provided, plot on this axis Returns ------- ax : matplotlib axis """ if ax is None: ax = plt.gca() # Intelligently label the support axis label_ax = bool(axlabel) if axlabel is None and hasattr(a, "name"): axlabel = a.name if axlabel is not None: label_ax = True # Make a a 1-d array a = np.asarray(a).squeeze() # Handle dictionary defaults if hist_kws is None: hist_kws = dict() if kde_kws is None: kde_kws = dict() if rug_kws is None: rug_kws = dict() if fit_kws is None: fit_kws = dict() # Get the color from the current color cycle if color is None: if vertical: line, = ax.plot(0, a.mean()) else: line, = ax.plot(a.mean(), 0) color = line.get_color() line.remove() if hist: if bins is None: bins = _freedman_diaconis_bins(a) hist_alpha = hist_kws.pop("alpha", 0.4) orientation = "horizontal" if vertical else "vertical" hist_color = hist_kws.pop("color", color) ax.hist(a, bins, normed=True, color=hist_color, alpha=hist_alpha, orientation=orientation, **hist_kws) if kde: kde_color = kde_kws.pop("color", color) kdeplot(a, vertical=vertical, color=kde_color, ax=ax, **kde_kws) if rug: rug_color = rug_kws.pop("color", color) axis = "y" if vertical else "x" rugplot(a, axis=axis, color=rug_color, ax=ax, **rug_kws) if fit is not None: fit_color = fit_kws.pop("color", "#282828") gridsize = fit_kws.pop("gridsize", 500) cut = fit_kws.pop("cut", 3) clip = fit_kws.pop("clip", (-np.inf, np.inf)) bw = sm.nonparametric.bandwidths.bw_scott(a) x = _kde_support(a, bw, gridsize, cut, clip) params = fit.fit(a) pdf = lambda x: fit.pdf(x, *params) y = pdf(x) if vertical: x, y = y, x ax.plot(x, y, color=fit_color, **fit_kws) if label_ax: if vertical: ax.set_ylabel(axlabel) else: ax.set_xlabel(axlabel) return ax
def kdeplot(a, shade=False, npts=1000, support_thresh=1e-4, support_min=-np.inf, support_max=np.inf, vertical=False, ax=None, **kwargs): """Calculate and plot a one-dimentional kernel density estimate. Parameters ---------- a : ndarray Input data. shade : bool, optional If true, shade in the area under the KDE curve. npts : int, optional Number of points in the evaluation grid. support_thresh : float, optional Draw density for values up to support_thresh * max(density). support_{min, max}: floats, optional If provided, do not draw above or below these values (does not affect the actual estimation) vertical : bool If True, density is on x-axis. ax : matplotlib axis, optional Axis to plot on, otherwise uses current axis. kwargs : other keyword arguments for plot() Returns ------- ax : matplotlib axis Axis with plot. """ if ax is None: ax = plt.gca() # Check if a label was specified in the call label = kwargs.pop("label", None) # Otherwise check if the data object has a name if label is None and hasattr(a, "name"): label = a.name # Decide if we're going to add a legend legend = not label is None label = "_nolegend_" if label is None else label # Compute the KDE a = np.asarray(a) kde = stats.gaussian_kde(a.astype(float).ravel()) x = _kde_support(a, kde, npts, support_thresh) x = x[x >= support_min] x = x[x <= support_max] y = kde(x) if vertical: y, x = x, y # Find a color for the plot in a way that uses the active color cycle line, = ax.plot(x, y, **kwargs) color = line.get_color() line.remove() kwargs.pop("color", None) # Draw the KDE plot and, optionally, shade ax.plot(x, y, color=color, label=label, **kwargs) if shade: ax.fill_between(x, 1e-12, y, color=color, alpha=0.25) # Draw the legend here if legend: ax.legend(loc="best") return ax
def violin(vals, groupby=None, inner="box", color=None, positions=None, names=None, widths=.8, alpha=None, join_rm=False, kde_thresh=1e-2, inner_kws=None, ax=None, **kwargs): """Create a violin plot (a combination of boxplot and KDE plot). Parameters ---------- vals : array or sequence of arrays data to plot groupby : grouping object if `vals` is a Series, this is used to group inner : box | sticks | points plot quartiles or individual sample values inside violin color : mpl color, sequence of colors, or seaborn palette name inner violin colors positions : number or sequence of numbers position of first violin or positions of each violin widths : float width of each violin at maximum density alpha : float, optional transparancy of violin fill join_rm : boolean, optional if True, positions in the input arrays are treated as repeated measures and are joined with a line plot names : list of strings, optional names to plot on x axis, otherwise plots numbers kde_thresh : float, optional proportion of maximum at which to threshold the KDE curve inner_kws : dict, optional keyword arugments for inner plot ax : matplotlib axis, optional axis to plot on, otherwise creates new one Returns ------- ax : matplotlib axis axis with violin plot """ if ax is None: ax = plt.gca() if isinstance(vals, pd.DataFrame): if names is None: names = vals.columns if vals.columns.name is not None: xlabel = vals.columns.name else: xlabel = None ylabel = None vals = vals.values elif isinstance(vals, pd.Series) and groupby is not None: if hasattr(groupby, "name"): xlabel = groupby.name ylabel = vals.name grouped_vals = pd.groupby(vals, groupby).values if names is None: names = grouped_vals.index vals = grouped_vals.values else: xlabel = None ylabel = None if hasattr(vals, 'shape'): if len(vals.shape) == 1: if hasattr(vals[0], 'shape'): vals = list(vals) else: vals = [vals] elif len(vals.shape) == 2: nr, nc = vals.shape if nr == 1: vals = [vals] elif nc == 1: vals = [vals.ravel()] else: vals = [vals[:, i] for i in xrange(nc)] else: raise ValueError("Input x can have no more than 2 dimensions") if not hasattr(vals[0], '__len__'): vals = [vals] vals = [np.asarray(a, float) for a in vals] if color is None: colors = husl_palette(len(vals), l=.7) else: if hasattr(color, "__iter__") and not isinstance(color, tuple): colors = color else: try: color = mpl.colors.colorConverter.to_rgb(color) colors = [color for _ in vals] except ValueError: colors = color_palette(color, len(vals)) colors = [mpl.colors.colorConverter.to_rgb(c) for c in colors] colors = [desaturate(c, .7) for c in colors] light_vals = [colorsys.rgb_to_hls(*c)[1] for c in colors] l = min(light_vals) * .6 gray = (l, l, l) if inner_kws is None: inner_kws = {} if positions is None: positions = np.arange(1, len(vals) + 1) elif not hasattr(positions, "__iter__"): positions = np.arange(positions, len(vals) + positions) in_alpha = inner_kws.pop("alpha", .6 if inner == "points" else 1) in_alpha *= 1 if alpha is None else alpha in_color = inner_kws.pop("color", gray) in_marker = inner_kws.pop("marker", ".") in_lw = inner_kws.pop("lw", 1.5 if inner == "box" else .8) for i, a in enumerate(vals): x = positions[i] kde = stats.gaussian_kde(a) y = _kde_support(a, kde, 1000, kde_thresh) dens = kde(y) scl = 1 / (dens.max() / (widths / 2)) dens *= scl ax.fill_betweenx(y, x - dens, x + dens, alpha=alpha, color=colors[i]) if inner == "box": for quant in moss.percentiles(a, [25, 75]): q_x = kde(quant) * scl q_x = [x - q_x, x + q_x] ax.plot(q_x, [quant, quant], color=in_color, linestyle=":", linewidth=in_lw, **inner_kws) med = np.median(a) m_x = kde(med) * scl m_x = [x - m_x, x + m_x] ax.plot(m_x, [med, med], color=in_color, linestyle="--", linewidth=in_lw, **inner_kws) elif inner == "stick": x_vals = kde(a) * scl x_vals = [x - x_vals, x + x_vals] ax.plot(x_vals, [a, a], color=in_color, linewidth=in_lw, alpha=in_alpha, **inner_kws) elif inner == "points": x_vals = [x for _ in a] ax.plot(x_vals, a, in_marker, color=in_color, alpha=in_alpha, mew=0, **inner_kws) for side in [-1, 1]: ax.plot((side * dens) + x, y, c=gray, linewidth=1.5) if join_rm: ax.plot(range(1, len(vals) + 1), vals, color=in_color, alpha=2. / 3) ax.set_xticks(positions) if names is not None: if len(vals) != len(names): raise ValueError("Length of names list must match nuber of bins") ax.set_xticklabels(names) ax.set_xlim(positions[0] - .5, positions[-1] + .5) if xlabel is not None: ax.set_xlabel(xlabel) if ylabel is not None: ax.set_ylabel(ylabel) ax.xaxis.grid(False) return ax
def distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, hist_kws=None, kde_kws=None, rug_kws=None, fit_kws=None, color=None, vertical=False, xlabel=None, ax=None): """Flexibly plot a distribution of observations. Parameter a : (squeezable to) 1d array Observed data. bins : argument for matplotlib hist(), or None Specification of hist bins, or None to use Freedman-Diaconis rule. hist : bool, default True Whether to plot a (normed) histogram. kde : bool, default True Whether to plot a gaussian kernel density estimate. rug : bool, default False Whether to draw a rugplot on the support axis. fit : random variable object An object with `fit` method, returning a tuple that can be passed to a `pdf` method a positional arguments following an grid of values to evaluate the pdf on. {hist, kde, rug, fit}_kws : dictionaries Keyword arguments for underlying plotting functions. color : matplotlib color, optional Color to plot everything but the fitted curve in. vertical : bool, default False If True, oberved values are on y-axis. xlabel : string, False, or None Name for the x axis label. if None, will try to get it from a.name if False, do not set the x label. ax : matplotlib axis, optional if provided, plot on this axis Returns ------- ax : matplotlib axis """ if ax is None: ax = plt.gca() # Intelligently label the axis label_x = bool(xlabel) if xlabel is None and hasattr(a, "name"): xlabel = a.name if xlabel is not None: label_x = True # Make a a 1-d array a = np.asarray(a).squeeze() # Handle dictionary defaults if hist_kws is None: hist_kws = dict() if kde_kws is None: kde_kws = dict() if rug_kws is None: rug_kws = dict() if fit_kws is None: fit_kws = dict() # Get the color from the current color cycle if color is None: if vertical: line, = ax.plot(0, a.mean()) else: line, = ax.plot(a.mean(), 0) color = line.get_color() line.remove() if hist: if bins is None: # From http://stats.stackexchange.com/questions/798/ h = 2 * moss.iqr(a) * len(a)**-(1 / 3) bins = (a.max() - a.min()) / h hist_alpha = hist_kws.pop("alpha", 0.4) orientation = "horizontal" if vertical else "vertical" hist_color = hist_kws.pop("color", color) ax.hist(a, bins, normed=True, color=hist_color, alpha=hist_alpha, orientation=orientation, **hist_kws) if kde: kde_color = kde_kws.pop("color", color) kdeplot(a, vertical=vertical, color=kde_color, ax=ax, **kde_kws) if rug: rug_color = rug_kws.pop("color", color) axis = "y" if vertical else "x" rugplot(a, axis=axis, color=rug_color, ax=ax, **rug_kws) if fit is not None: fit_color = fit_kws.pop("color", "#282828") npts = fit_kws.pop("npts", 1000) support_thresh = fit_kws.pop("support_thresh", 1e-4) params = fit.fit(a) pdf = lambda x: fit.pdf(x, *params) x = _kde_support(a, pdf, npts, support_thresh) y = pdf(x) if vertical: x, y = y, x ax.plot(x, y, color=fit_color, **fit_kws) if label_x: ax.set_xlabel(xlabel) return ax
def distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, hist_kws=None, kde_kws=None, rug_kws=None, fit_kws=None, color=None, vertical=False, legend=False, xlabel=None, ax=None): """Flexibly plot a distribution of observations. Parameters ---------- a : (squeezable to) 1d array observed data bins : argument for matplotlib hist(), or None specification of bins or None to use Freedman-Diaconis rule hist : bool, default True whether to plot a (normed) histogram kde : bool, defualt True whether to plot a gaussian kernel density estimate rug : bool, default False whether to draw a rugplot on the support axis fit : random variable object object with `fit` method returning a tuple that can be passed to a `pdf` method a positional arguments following an array of values to evaluate the pdf at {hist, kde, rug, fit}_kws : dictionaries keyword arguments for underlying plotting functions color : matplotlib color, optional color to plot everything but the fitted curve in vertical : bool, default False if True, oberved values are on y-axis legend : bool, default True if True, add a legend to the plot with what the plotted lines are xlabel : string, False, or None name for the x axis label. if None, will try to get it from a.name if False, do not set the x label ax : matplotlib axis, optional if provided, plot on this axis Returns ------- ax : matplotlib axis """ if ax is None: ax = plt.gca() # Intelligently label the axis label_x = bool(xlabel) if xlabel is None and hasattr(a, "name"): xlabel = a.name if xlabel is not None: label_x = True # Make a a 1-d array a = np.asarray(a).squeeze() # Handle dictionary defaults if hist_kws is None: hist_kws = dict() if kde_kws is None: kde_kws = dict() if rug_kws is None: rug_kws = dict() if fit_kws is None: fit_kws = dict() # Get the color from the current color cycle if color is None: if vertical: line, = ax.plot(0, a.mean()) else: line, = ax.plot(a.mean(), 0) color = line.get_color() line.remove() if hist: if bins is None: # From http://stats.stackexchange.com/questions/798/ h = 2 * moss.iqr(a) * len(a) ** -(1 / 3) bins = (a.max() - a.min()) / h hist_alpha = hist_kws.pop("alpha", 0.4) orientation = "horizontal" if vertical else "vertical" hist_color = hist_kws.pop("color", color) ax.hist(a, bins, normed=True, color=hist_color, alpha=hist_alpha, orientation=orientation, **hist_kws) if kde: kde_color = kde_kws.pop("color", color) kde_kws["label"] = "kde" kdeplot(a, vertical=vertical, color=kde_color, ax=ax, **kde_kws) if rug: rug_color = rug_kws.pop("color", color) axis = "y" if vertical else "x" rugplot(a, axis=axis, color=rug_color, ax=ax, **rug_kws) if fit is not None: fit_color = fit_kws.pop("color", "#282828") npts = fit_kws.pop("npts", 1000) support_thresh = fit_kws.pop("support_thresh", 1e-4) params = fit.fit(a) pdf = lambda x: fit.pdf(x, *params) x = _kde_support(a, pdf, npts, support_thresh) y = pdf(x) if vertical: x, y = y, x fit_kws["label"] = fit.name ax.plot(x, y, color=fit_color, **fit_kws) if legend: ax.legend(loc="best") if label_x: ax.set_xlabel(xlabel) return ax
def violin(vals, groupby=None, inner="box", color=None, positions=None, names=None, widths=.8, alpha=None, join_rm=False, kde_thresh=1e-2, inner_kws=None, ax=None, **kwargs): """Create a violin plot (a combination of boxplot and KDE plot). Parameters ---------- vals : array or sequence of arrays data to plot groupby : grouping object if `vals` is a Series, this is used to group inner : box | sticks | points plot quartiles or individual sample values inside violin color : mpl color, sequence of colors, or seaborn palette name inner violin colors positions : number or sequence of numbers position of first violin or positions of each violin widths : float width of each violin at maximum density alpha : float, optional transparancy of violin fill join_rm : boolean, optional if True, positions in the input arrays are treated as repeated measures and are joined with a line plot names : list of strings, optional names to plot on x axis, otherwise plots numbers kde_thresh : float, optional proportion of maximum at which to threshold the KDE curve inner_kws : dict, optional keyword arugments for inner plot ax : matplotlib axis, optional axis to plot on, otherwise creates new one Returns ------- ax : matplotlib axis axis with violin plot """ if ax is None: ax = plt.gca() if isinstance(vals, pd.DataFrame): if names is None: names = vals.columns if vals.columns.name is not None: xlabel = vals.columns.name else: xlabel = None ylabel = None vals = vals.values elif isinstance(vals, pd.Series) and groupby is not None: if hasattr(groupby, "name"): xlabel = groupby.name if names is None: names = np.sort(pd.unique(groupby)) ylabel = vals.name grouped_vals = pd.groupby(vals, groupby).values vals = grouped_vals.values else: xlabel = None ylabel = None if hasattr(vals, 'shape'): if len(vals.shape) == 1: if hasattr(vals[0], 'shape'): vals = list(vals) else: vals = [vals] elif len(vals.shape) == 2: nr, nc = vals.shape if nr == 1: vals = [vals] elif nc == 1: vals = [vals.ravel()] else: vals = [vals[:, i] for i in xrange(nc)] else: raise ValueError("Input x can have no more than 2 dimensions") if not hasattr(vals[0], '__len__'): vals = [vals] vals = [np.asarray(a, float) for a in vals] if color is None: colors = husl_palette(len(vals), l=.7) else: if hasattr(color, "__iter__") and not isinstance(color, tuple): colors = color else: try: color = mpl.colors.colorConverter.to_rgb(color) colors = [color for _ in vals] except ValueError: colors = color_palette(color, len(vals)) colors = [mpl.colors.colorConverter.to_rgb(c) for c in colors] colors = [desaturate(c, .7) for c in colors] light_vals = [colorsys.rgb_to_hls(*c)[1] for c in colors] l = min(light_vals) * .6 gray = (l, l, l) if inner_kws is None: inner_kws = {} if positions is None: positions = np.arange(1, len(vals) + 1) elif not hasattr(positions, "__iter__"): positions = np.arange(positions, len(vals) + positions) in_alpha = inner_kws.pop("alpha", .6 if inner == "points" else 1) in_alpha *= 1 if alpha is None else alpha in_color = inner_kws.pop("color", gray) in_marker = inner_kws.pop("marker", ".") in_lw = inner_kws.pop("lw", 1.5 if inner == "box" else .8) for i, a in enumerate(vals): x = positions[i] kde = stats.gaussian_kde(a) y = _kde_support(a, kde, 1000, kde_thresh) dens = kde(y) scl = 1 / (dens.max() / (widths / 2)) dens *= scl ax.fill_betweenx(y, x - dens, x + dens, alpha=alpha, color=colors[i]) if inner == "box": for quant in moss.percentiles(a, [25, 75]): q_x = kde(quant) * scl q_x = [x - q_x, x + q_x] ax.plot(q_x, [quant, quant], color=in_color, linestyle=":", linewidth=in_lw, **inner_kws) med = np.median(a) m_x = kde(med) * scl m_x = [x - m_x, x + m_x] ax.plot(m_x, [med, med], color=in_color, linestyle="--", linewidth=in_lw, **inner_kws) elif inner == "stick": x_vals = kde(a) * scl x_vals = [x - x_vals, x + x_vals] ax.plot(x_vals, [a, a], color=in_color, linewidth=in_lw, alpha=in_alpha, **inner_kws) elif inner == "points": x_vals = [x for _ in a] ax.plot(x_vals, a, in_marker, color=in_color, alpha=in_alpha, mew=0, **inner_kws) for side in [-1, 1]: ax.plot((side * dens) + x, y, c=gray, linewidth=1.5) if join_rm: ax.plot(range(1, len(vals) + 1), vals, color=in_color, alpha=2. / 3) ax.set_xticks(positions) if names is not None: if len(vals) != len(names): raise ValueError("Length of names list must match nuber of bins") ax.set_xticklabels(names) ax.set_xlim(positions[0] - .5, positions[-1] + .5) if xlabel is not None: ax.set_xlabel(xlabel) if ylabel is not None: ax.set_ylabel(ylabel) ax.xaxis.grid(False) return ax