def plot(self, experiment, data, **kwargs): """ Base function for facetted plotting Parameters ---------- experiment: Experiment The :class:`.Experiment` to plot using this view. title : str Set the plot title xlabel, ylabel : str Set the X and Y axis labels huelabel : str Set the label for the hue facet (in the legend) legend : bool Plot a legend for the color or hue facet? Defaults to `True`. sharex, sharey : bool If there are multiple subplots, should they share axes? Defaults to `True`. col_wrap : int If `xfacet` is set and `yfacet` is not set, you can "wrap" the subplots around so that they form a multi-row grid by setting `col_wrap` to the number of columns you want. sns_style : {"darkgrid", "whitegrid", "dark", "white", "ticks"} Which `seaborn` style to apply to the plot? Default is `whitegrid`. sns_context : {"paper", "notebook", "talk", "poster"} Which `seaborn` context to use? Controls the scaling of plot elements such as tick labels and the legend. Default is `talk`. despine : Bool Remove the top and right axes from the plot? Default is `True`. Other Parameters ---------------- cmap : matplotlib colormap If plotting a huefacet with many values, use this color map instead of the default. norm : matplotlib.colors.Normalize If plotting a huefacet with many values, use this object for color scale normalization. """ if experiment is None: raise util.CytoflowViewError('experiment', "No experiment specified") col_wrap = kwargs.pop('col_wrap', None) if col_wrap is not None and self.yfacet: raise util.CytoflowViewError( 'yfacet', "Can't set yfacet and col_wrap at the same time.") if col_wrap is not None and not self.xfacet: raise util.CytoflowViewError('xfacet', "Must set xfacet to use col_wrap.") if col_wrap is not None and col_wrap < 2: raise util.CytoflowViewError(None, "col_wrap must be None or > 1") title = kwargs.pop("title", None) xlabel = kwargs.pop("xlabel", None) ylabel = kwargs.pop("ylabel", None) huelabel = kwargs.pop("huelabel", self.huefacet) sharex = kwargs.pop("sharex", True) sharey = kwargs.pop("sharey", True) legend = kwargs.pop('legend', True) sns_style = kwargs.pop('sns_style', 'whitegrid') sns_context = kwargs.pop('sns_context', 'talk') despine = kwargs.pop('despine', False) cols = col_wrap if col_wrap else \ len(data[self.xfacet].unique()) if self.xfacet else 1 sns.set_style(sns_style) sns.set_context(sns_context) g = sns.FacetGrid(data, height=6 / cols, aspect=1.5, col=(self.xfacet if self.xfacet else None), row=(self.yfacet if self.yfacet else None), hue=(self.huefacet if self.huefacet else None), col_order=(np.sort(data[self.xfacet].unique()) if self.xfacet else None), row_order=(np.sort(data[self.yfacet].unique()) if self.yfacet else None), hue_order=(np.sort(data[self.huefacet].unique()) if self.huefacet else None), col_wrap=col_wrap, legend_out=False, sharex=sharex, sharey=sharey) plot_ret = self._grid_plot(experiment=experiment, grid=g, **kwargs) kwargs.update(plot_ret) xscale = kwargs.pop("xscale", None) yscale = kwargs.pop("yscale", None) xlim = kwargs.pop("xlim", None) ylim = kwargs.pop("ylim", None) for ax in g.axes.flatten(): if xscale: ax.set_xscale(xscale.name, **xscale.get_mpl_params(ax.get_xaxis())) if yscale: ax.set_yscale(yscale.name, **yscale.get_mpl_params(ax.get_yaxis())) if xlim: ax.set_xlim(xlim) if ylim: ax.set_ylim(ylim) # if we are sharing x axes, make sure the x limits are the same for each if sharex: fig = plt.gcf() fig_x_min = float("inf") fig_x_max = float("-inf") for ax in fig.get_axes(): ax_x_min, ax_x_max = ax.get_xlim() if ax_x_min < fig_x_min: fig_x_min = ax_x_min if ax_x_max > fig_x_max: fig_x_max = ax_x_max for ax in fig.get_axes(): ax.set_xlim(fig_x_min, fig_x_max) # if we are sharing y axes, make sure the y limits are the same for each if sharey: fig = plt.gcf() fig_y_max = float("-inf") for ax in fig.get_axes(): _, ax_y_max = ax.get_ylim() if ax_y_max > fig_y_max: fig_y_max = ax_y_max for ax in fig.get_axes(): ax.set_ylim(None, fig_y_max) # if we have a hue facet and a lot of hues, make a color bar instead # of a super-long legend. cmap = kwargs.pop('cmap', None) norm = kwargs.pop('norm', None) legend_data = kwargs.pop('legend_data', None) if legend: if cmap and norm: plot_ax = plt.gca() cax, _ = mpl.colorbar.make_axes(plt.gcf().get_axes()) mpl.colorbar.ColorbarBase(cax, cmap, norm) plt.sca(plot_ax) elif self.huefacet: current_palette = mpl.rcParams['axes.prop_cycle'] if util.is_numeric(data[self.huefacet]) and \ len(g.hue_names) > len(current_palette): cmap = mpl.colors.ListedColormap( sns.color_palette("husl", n_colors=len(g.hue_names))) hue_scale = util.scale_factory( self.huescale, experiment, data=data[self.huefacet].values) plot_ax = plt.gca() cax, _ = mpl.colorbar.make_axes(plt.gcf().get_axes()) mpl.colorbar.ColorbarBase(cax, cmap=cmap, norm=hue_scale.norm(), label=huelabel) plt.sca(plot_ax) else: g.add_legend(title=huelabel, legend_data=legend_data) ax = g.axes.flat[0] legend = ax.legend_ self._update_legend(legend) # for lh in legend.legendHandles: # lh.set_facecolor(lh.get_facecolor()) # i don't know why # lh.set_edgecolor(lh.get_edgecolor()) # these are needed # lh.set_alpha(1.0) if title: if self.xfacet or self.yfacet: plt.subplots_adjust(top=0.9) else: plt.subplots_adjust(top=0.94) plt.suptitle(title) if xlabel == "": xlabel = None if ylabel == "": ylabel = None g.set_axis_labels(xlabel, ylabel) sns.despine(top=despine, right=despine, bottom=False, left=False)
def _make_data(self, experiment): if experiment is None: raise util.CytoflowViewError('experiment', "No experiment specified") if not self.xstatistic: raise util.CytoflowViewError('xstatistic', "X Statistic not set") if self.xstatistic not in experiment.statistics: raise util.CytoflowViewError( 'xstatistic', "Can't find the statistic {} in the experiment".format( self.xstatistic)) else: xstat = experiment.statistics[self.xstatistic] if not util.is_numeric(xstat): raise util.CytoflowViewError('xstatistic', "X statistic must be numeric") if self.x_error_statistic[0]: if self.x_error_statistic not in experiment.statistics: raise util.CytoflowViewError( 'x_error_statistic', "Can't find the X error statistic in the experiment") else: x_error_stat = experiment.statistics[self.x_error_statistic] else: x_error_stat = None if x_error_stat is not None: if set(xstat.index.names) != set(x_error_stat.index.names): raise util.CytoflowViewError( 'x_error_statistic', "X data statistic and error statistic " "don't have the same index.") try: x_error_stat.index = x_error_stat.index.reorder_levels( xstat.index.names) x_error_stat.sort_index(inplace=True) except AttributeError: pass if not xstat.index.equals(x_error_stat.index): raise util.CytoflowViewError( 'x_error_statistic', "X data statistic and error statistic " " don't have the same index.") if xstat.name == x_error_stat.name: raise util.CytoflowViewError( 'x_error_statistic', "X data statistic and error statistic can " "not have the same name.") if not self.ystatistic: raise util.CytoflowViewError('ystatistic', "Y statistic not set") if self.ystatistic not in experiment.statistics: raise util.CytoflowViewError( 'ystatistic', "Can't find the Y statistic {} in the experiment".format( self.ystatistic)) else: ystat = experiment.statistics[self.ystatistic] if not util.is_numeric(ystat): raise util.CytoflowViewError('ystatistic', "Y statistic must be numeric") if self.y_error_statistic[0]: if self.y_error_statistic not in experiment.statistics: raise util.CytoflowViewError( 'y_error_statistic', "Can't find the Y error statistic in the experiment") else: y_error_stat = experiment.statistics[self.y_error_statistic] else: y_error_stat = None if y_error_stat is not None: if set(ystat.index.names) != set(y_error_stat.index.names): raise util.CytoflowViewError( 'y_error_statistic', "Y data statistic and error statistic " "don't have the same index.") try: y_error_stat.index = y_error_stat.index.reorder_levels( ystat.index.names) y_error_stat.sort_index(inplace=True) except AttributeError: pass if not ystat.index.equals(y_error_stat.index): raise util.CytoflowViewError( 'y_error_statistic', "Y data statistic and error statistic " " don't have the same index.") if ystat.name == y_error_stat.name: raise util.CytoflowViewError( 'y_error_statistic', "Data statistic and error statistic can " "not have the same name.") if xstat.name == ystat.name: raise util.CytoflowViewError( 'ystatistic', "X and Y statistics can " "not have the same name.") if set(xstat.index.names) != set(ystat.index.names): raise util.CytoflowViewError( 'ystatistic', "X and Y data statistics " "don't have the same index.") try: ystat.index = ystat.index.reorder_levels(xstat.index.names) ystat.sort_index(inplace=True) except AttributeError: pass intersect_idx = xstat.index.intersection(ystat.index) xstat = xstat.reindex(intersect_idx) xstat.sort_index(inplace=True) ystat = ystat.reindex(intersect_idx) ystat.sort_index(inplace=True) data = pd.DataFrame(index=xstat.index) data[xstat.name] = xstat data[ystat.name] = ystat if x_error_stat is not None: data[x_error_stat.name] = x_error_stat if y_error_stat is not None: data[y_error_stat.name] = y_error_stat return data
def plot(self, experiment, **kwargs): """Plot a faceted histogram view of a channel""" if not experiment: raise util.CytoflowViewError("No experiment specified") if not self.channel: raise util.CytoflowViewError("Must specify a channel") if self.channel not in experiment.data: raise util.CytoflowViewError( "Channel {0} not in the experiment".format(self.channel)) if self.xfacet and self.xfacet not in experiment.conditions: raise util.CytoflowViewError( "X facet {0} not in the experiment".format(self.xfacet)) if self.yfacet and self.yfacet not in experiment.conditions: raise util.CytoflowViewError( "Y facet {0} not in the experiment".format(self.yfacet)) if self.huefacet and self.huefacet not in experiment.conditions: raise util.CytoflowViewError( "Hue facet {0} not in the experiment".format(self.huefacet)) facets = filter(lambda x: x, [self.xfacet, self.yfacet, self.huefacet]) if len(facets) != len(set(facets)): raise util.CytoflowViewError("Can't reuse facets") col_wrap = kwargs.pop('col_wrap', None) if col_wrap and self.yfacet: raise util.CytoflowViewError( "Can't set yfacet and col_wrap at the same time.") if col_wrap and not self.xfacet: raise util.CytoflowViewError("Must set xfacet to use col_wrap.") if self.subset: try: data = experiment.query(self.subset).data.reset_index() except: raise util.CytoflowViewError( "Subset string '{0}' isn't valid".format(self.subset)) if len(data) == 0: raise util.CytoflowViewError( "Subset string '{0}' returned no events".format( self.subset)) else: data = experiment.data kwargs.setdefault('shade', True) kwargs['label'] = self.name # get the scale kwargs['scale'] = scale = util.scale_factory(self.scale, experiment, channel=self.channel) # adjust the limits to clip extreme values min_quantile = kwargs.pop("min_quantile", 0.001) max_quantile = kwargs.pop("max_quantile", 0.999) xlim = kwargs.pop("xlim", None) if xlim is None: xlim = (scale.clip(data[self.channel].quantile(min_quantile)), scale.clip(data[self.channel].quantile(max_quantile))) sharex = kwargs.pop('sharex', True) sharey = kwargs.pop('sharey', True) cols = col_wrap if col_wrap else \ len(data[self.xfacet].unique()) if self.xfacet else 1 g = sns.FacetGrid(data, size=(6 / cols), aspect=1.5, col=(self.xfacet if self.xfacet else None), row=(self.yfacet if self.yfacet else None), hue=(self.huefacet if self.huefacet else None), col_order=(np.sort(data[self.xfacet].unique()) if self.xfacet else None), row_order=(np.sort(data[self.yfacet].unique()) if self.yfacet else None), hue_order=(np.sort(data[self.huefacet].unique()) if self.huefacet else None), col_wrap=col_wrap, legend_out=False, sharex=sharex, sharey=sharey, xlim=xlim) # set the scale for each set of axes; can't just call plt.xscale() for ax in g.axes.flatten(): ax.set_xscale(self.scale, **scale.mpl_params) g.map(_univariate_kdeplot, self.channel, **kwargs) # adjust the limits to clip extreme values min_quantile = kwargs.pop("min_quantile", 0.001) max_quantile = kwargs.pop("max_quantile", 0.999) def autoscale_x(*args, **kwargs): d = args[0] plt.gca().set_xlim(d.quantile(min_quantile), d.quantile(max_quantile)) g.map(autoscale_x, self.channel) # if we are sharing y axes, make sure the y scale is the same for each if sharey: fig = plt.gcf() fig_y_max = float("-inf") for ax in fig.get_axes(): _, ax_y_max = ax.get_ylim() if ax_y_max > fig_y_max: fig_y_max = ax_y_max for ax in fig.get_axes(): ax.set_ylim(None, fig_y_max) # if we are sharing x axes, make sure the x scale is the same for each if sharex: fig = plt.gcf() fig_x_min = float("inf") fig_x_max = float("-inf") for ax in fig.get_axes(): ax_x_min, ax_x_max = ax.get_xlim() if ax_x_min < fig_x_min: fig_x_min = ax_x_min if ax_x_max > fig_x_max: fig_x_max = ax_x_max for ax in fig.get_axes(): ax.set_xlim(fig_x_min, fig_x_max) # if we have a hue facet and a lot of hues, make a color bar instead # of a super-long legend. if self.huefacet: current_palette = mpl.rcParams['axes.color_cycle'] if util.is_numeric(experiment.data[self.huefacet]) and \ len(g.hue_names) > len(current_palette): plot_ax = plt.gca() cmap = mpl.colors.ListedColormap( sns.color_palette("husl", n_colors=len(g.hue_names))) cax, _ = mpl.colorbar.make_axes(plt.gca()) hue_scale = util.scale_factory(self.huescale, experiment, condition=self.huefacet) mpl.colorbar.ColorbarBase(cax, cmap=cmap, norm=hue_scale.color_norm(), label=self.huefacet) plt.sca(plot_ax) else: g.add_legend(title=self.huefacet)
def plot(self, experiment, plot_name = None, **kwargs): """Plot a chart""" if not experiment: raise util.CytoflowViewError("No experiment specified") if not self.statistic: raise util.CytoflowViewError("Statistic not set") if self.statistic not in experiment.statistics: raise util.CytoflowViewError("Can't find the statistic {} in the experiment" .format(self.statistic)) else: stat = experiment.statistics[self.statistic] if self.error_statistic[0]: if self.error_statistic not in experiment.statistics: raise util.CytoflowViewError("Can't find the error statistic in the experiment") else: error_stat = experiment.statistics[self.error_statistic] else: error_stat = None if error_stat is not None: if not stat.index.equals(error_stat.index): raise util.CytoflowViewError("Data statistic and error statistic " " don't have the same index.") data = pd.DataFrame(index = stat.index) data[stat.name] = stat if error_stat is not None: error_name = util.random_string(6) data[error_name] = error_stat if self.subset: try: # TODO - either sanitize column names, or check to see that # all conditions are valid Python variables data = data.query(self.subset) except: raise util.CytoflowViewError("Subset string '{0}' isn't valid" .format(self.subset)) if len(data) == 0: raise util.CytoflowViewError("Subset string '{0}' returned no values" .format(self.subset)) names = list(data.index.names) for name in names: unique_values = data.index.get_level_values(name).unique() if len(unique_values) == 1: warn("Only one value for level {}; dropping it.".format(name), util.CytoflowViewWarning) try: data.index = data.index.droplevel(name) except AttributeError: raise util.CytoflowViewError("Must have more than one " "value to plot.") names = list(data.index.names) if not self.variable: raise util.CytoflowViewError("X variable not set") if self.variable not in experiment.conditions: raise util.CytoflowViewError("X variable {0} not in the experiment" .format(self.variable)) if self.variable not in names: raise util.CytoflowViewError("X variable {} is not a statistic index; " "must be one of {}".format(self.variable, names)) if experiment.conditions[self.variable].dtype.kind not in "biufc": raise util.CytoflowViewError("X variable {0} isn't numeric" .format(self.variable)) if self.xfacet and self.xfacet not in experiment.conditions: raise util.CytoflowViewError("X facet {0} not in the experiment") if self.xfacet and self.xfacet not in names: raise util.CytoflowViewError("X facet {} is not a statistic index; " "must be one of {}".format(self.xfacet, names)) if self.yfacet and self.yfacet not in experiment.conditions: raise util.CytoflowViewError("Y facet {0} not in the experiment") if self.yfacet and self.yfacet not in names: raise util.CytoflowViewError("Y facet {} is not a statistic index; " "must be one of {}".format(self.yfacet, names)) if self.huefacet and self.huefacet not in experiment.metadata: raise util.CytoflowViewError("Hue facet {0} not in the experiment") if self.huefacet and self.huefacet not in names: raise util.CytoflowViewError("Hue facet {} is not a statistic index; " "must be one of {}".format(self.huefacet, names)) col_wrap = kwargs.pop('col_wrap', None) if col_wrap and self.yfacet: raise util.CytoflowViewError("Can't set yfacet and col_wrap at the same time.") if col_wrap and not self.xfacet: raise util.CytoflowViewError("Must set xfacet to use col_wrap.") facets = filter(lambda x: x, [self.variable, self.xfacet, self.yfacet, self.huefacet]) if len(facets) != len(set(facets)): raise util.CytoflowViewError("Can't reuse facets") unused_names = list(set(names) - set(facets)) if unused_names and plot_name is None: for plot in self.enum_plots(experiment): self.plot(experiment, plot, **kwargs) return data.reset_index(inplace = True) if plot_name is not None: if plot_name is not None and not unused_names: raise util.CytoflowViewError("Plot {} not from plot_enum" .format(plot_name)) groupby = data.groupby(unused_names) if plot_name not in set(groupby.groups.keys()): raise util.CytoflowViewError("Plot {} not from plot_enum" .format(plot_name)) data = groupby.get_group(plot_name) data.reset_index(drop = True, inplace = True) xscale = util.scale_factory(self.xscale, experiment, condition = self.variable) if error_stat is not None: yscale = util.scale_factory(self.yscale, experiment, statistic = self.error_statistic) else: yscale = util.scale_factory(self.yscale, experiment, statistic = self.statistic) xlim = kwargs.pop("xlim", None) if xlim is None: xlim = (xscale.clip(data[self.variable].min() * 0.9), xscale.clip(data[self.variable].max() * 1.1)) ylim = kwargs.pop("ylim", None) if ylim is None: ylim = (yscale.clip(data[stat.name].min() * 0.9), yscale.clip(data[stat.name].max() * 1.1)) if error_stat is not None: try: ylim = (yscale.clip(min([x[0] for x in error_stat]) * 0.9), yscale.clip(max([x[1] for x in error_stat]) * 1.1)) except IndexError: ylim = (yscale.clip(error_stat.min() * 0.9), yscale.clip(error_stat.max() * 1.1)) kwargs.setdefault('antialiased', True) cols = col_wrap if col_wrap else \ len(data[self.xfacet].unique()) if self.xfacet else 1 sharex = kwargs.pop('sharex', True) sharey = kwargs.pop('sharey', True) grid = sns.FacetGrid(data, size = (6 / cols), aspect = 1.5, col = (self.xfacet if self.xfacet else None), row = (self.yfacet if self.yfacet else None), hue = (self.huefacet if self.huefacet else None), col_order = (np.sort(data[self.xfacet].unique()) if self.xfacet else None), row_order = (np.sort(data[self.yfacet].unique()) if self.yfacet else None), hue_order = (np.sort(data[self.huefacet].unique()) if self.huefacet else None), col_wrap = col_wrap, legend_out = False, sharex = sharex, sharey = sharey, xlim = xlim, ylim = ylim) for ax in grid.axes.flatten(): ax.set_xscale(self.xscale, **xscale.mpl_params) ax.set_yscale(self.yscale, **yscale.mpl_params) # plot the error bars first so the axis labels don't get overwritten if error_stat is not None: grid.map(_error_bars, self.variable, stat.name, error_name, **kwargs) grid.map(plt.plot, self.variable, stat.name, **kwargs) # if we are sharing y axes, make sure the y scale is the same for each if sharey: fig = plt.gcf() fig_y_min = float("inf") fig_y_max = float("-inf") for ax in fig.get_axes(): ax_y_min, ax_y_max = ax.get_ylim() if ax_y_min < fig_y_min: fig_y_min = ax_y_min if ax_y_max > fig_y_max: fig_y_max = ax_y_max for ax in fig.get_axes(): ax.set_ylim(fig_y_min, fig_y_max) # if we are sharing x axes, make sure the x scale is the same for each if sharex: fig = plt.gcf() fig_x_min = float("inf") fig_x_max = float("-inf") for ax in fig.get_axes(): ax_x_min, ax_x_max = ax.get_xlim() if ax_x_min < fig_x_min: fig_x_min = ax_x_min if ax_x_max > fig_x_max: fig_x_max = ax_x_max for ax in fig.get_axes(): ax.set_xlim(fig_x_min, fig_x_max) # if we have a hue facet and a lot of hues, make a color bar instead # of a super-long legend. if self.huefacet: current_palette = mpl.rcParams['axes.color_cycle'] if util.is_numeric(experiment.data[self.huefacet]) and \ len(grid.hue_names) > len(current_palette): plot_ax = plt.gca() cmap = mpl.colors.ListedColormap(sns.color_palette("husl", n_colors = len(grid.hue_names))) cax, kw = mpl.colorbar.make_axes(plt.gca()) norm = mpl.colors.Normalize(vmin = np.min(grid.hue_names), vmax = np.max(grid.hue_names), clip = False) mpl.colorbar.ColorbarBase(cax, cmap = cmap, norm = norm, label = self.huefacet, **kw) plt.sca(plot_ax) else: grid.add_legend(title = self.huefacet) if unused_names and plot_name: plt.title("{0} = {1}".format(unused_names, plot_name)) plt.ylabel(self.statistic)
def plot(self, experiment, plot_name = None, **kwargs): """Plot a chart of a variable's values against a statistic. Parameters ---------- variable_lim : (float, float) The limits on the variable axis color : a matplotlib color The color to plot with. Overridden if `huefacet` is not `None` linewidth : float The width of the line, in points linestyle : ['solid' | 'dashed', 'dashdot', 'dotted' | (offset, on-off-dash-seq) | '-' | '--' | '-.' | ':' | 'None' | ' ' | ''] marker : a matplotlib marker style See http://matplotlib.org/api/markers_api.html#module-matplotlib.markers markersize : int The marker size in points markerfacecolor : a matplotlib color The color to make the markers. Overridden (?) if `huefacet` is not `None` alpha : the alpha blending value, from 0.0 (transparent) to 1.0 (opaque) capsize : scalar The size of the error bar caps, in points shade_error : bool If `False` (the default), plot the error statistic as traditional "error bars." If `True`, plot error statistic as a filled, shaded region. shade_alpha : float The transparency of the shaded error region, from 0.0 (transparent) to 1.0 (opaque.) Default is 0.2. Notes ----- Other `kwargs` are passed to `matplotlib.pyplot.plot <https://matplotlib.org/devdocs/api/_as_gen/matplotlib.pyplot.plot.html>`_ """ if experiment is None: raise util.CytoflowViewError('experiment', "No experiment specified") if self.variable not in experiment.conditions: raise util.CytoflowError('variable', "Variable {} not in the experiment" .format(self.variable)) if not util.is_numeric(experiment[self.variable]): raise util.CytoflowError('variable', "Variable {} must be numeric" .format(self.variable)) variable_scale = util.scale_factory(self.variable_scale, experiment, condition = self.variable) super().plot(experiment, plot_name, variable_scale = variable_scale, **kwargs)