class Kde2DPlotParams(Data2DPlotParams): shade = Bool(False) min_alpha = util.PositiveCFloat(0.2, allow_zero = False) max_alpha = util.PositiveCFloat(0.9, allow_zero = False) n_levels = util.PositiveCInt(10, allow_zero = False) bw = Enum(['scott', 'silverman']) gridsize = util.PositiveCInt(50, allow_zero = False) def default_traits_view(self): base_view = Data2DPlotParams.default_traits_view(self) return View(Item('shade'), Item('min_alpha', editor = TextEditor(auto_set = False)), Item('max_alpha', editor = TextEditor(auto_set = False)), Item('n_levels', editor = TextEditor(auto_set = False), label = "Num\nlevels"), Item('bw', label = "Bandwidth"), Item('gridsize', editor = TextEditor(auto_set = False), label = "Grid size"), base_view.content)
class Kde2DPlotParams(Data2DPlotParams): shade = Bool(False) min_alpha = util.PositiveCFloat(0.2, allow_zero=False) max_alpha = util.PositiveCFloat(0.9, allow_zero=False) n_levels = util.PositiveCInt(10, allow_zero=False) bw = Enum(['scott', 'silverman']) gridsize = util.PositiveCInt(50, allow_zero=False)
class Kde1DPlotParams(Data1DPlotParams): shade = Bool(True) alpha = util.PositiveCFloat(0.25) kernel = Enum([ 'gaussian', 'tophat', 'epanechnikov', 'exponential', 'linear', 'cosine' ]) bw = Enum(['scott', 'silverman']) gridsize = util.PositiveCInt(100, allow_zero=False) linestyle = Enum(LINE_STYLES) linewidth = util.PositiveCFloat(2, allow_zero=True) def default_traits_view(self): base_view = Data1DPlotParams.default_traits_view(self) return View( Item('shade'), Item('alpha', editor=TextEditor(auto_set=False)), Item('kernel'), Item('bw', label="Bandwidth"), Item('gridsize', editor=TextEditor(auto_set=False), label="Grid size"), Item('linestyle'), Item('linewidth', editor=TextEditor(auto_set=False, format_func=lambda x: "" if x == None else str(x))), base_view.content)
class HistogramPlotParams(Data1DPlotParams): num_bins = util.PositiveCInt(None, allow_none=True) histtype = Enum(['stepfilled', 'step', 'bar']) linestyle = Enum(LINE_STYLES) linewidth = util.PositiveCFloat(None, allow_none=True, allow_zero=True) density = Bool(False) alpha = util.PositiveCFloat(0.5, allow_zero=True)
class Kde1DPlotParams(Data1DPlotParams): shade = Bool(True) alpha = util.PositiveCFloat(0.25) kernel = Enum([ 'gaussian', 'tophat', 'epanechnikov', 'exponential', 'linear', 'cosine' ]) bw = Enum(['scott', 'silverman']) gridsize = util.PositiveCInt(100, allow_zero=False) linestyle = Enum(LINE_STYLES) linewidth = util.PositiveCFloat(2, allow_zero=True)
class BasePlotParams(HasTraits): title = Str xlabel = Str ylabel = Str huelabel = Str col_wrap = util.PositiveCInt(None, allow_zero = False, allow_none = True) sns_style = Enum(['whitegrid', 'darkgrid', 'white', 'dark', 'ticks']) sns_context = Enum(['talk', 'poster', 'notebook', 'paper']) legend = Bool(True) sharex = Bool(True) sharey = Bool(True) despine = Bool(True) def default_traits_view(self): return View( Item('title', editor = TextEditor(auto_set = False)), Item('xlabel', label = "X label", editor = TextEditor(auto_set = False)), Item('ylabel', label = "Y label", editor = TextEditor(auto_set = False)), Item('huelabel', label = "Hue label", editor = TextEditor(auto_set = False)), Item('col_wrap', label = "Columns", editor = TextEditor(auto_set = False, format_func = lambda x: "" if x == None else str(x))), Item('sns_style', label = "Style"), Item('sns_context', label = "Context"), Item('legend'), Item('sharex', label = "Share\nX axis?"), Item('sharey', label = "Share\nY axis?"), Item('despine', label = "Despine?")) def __repr__(self): return traits_repr(self)
class DensityPlotParams(Data2DPlotParams): gridsize = util.PositiveCInt(50, allow_zero=False) smoothed = Bool(False) smoothed_sigma = util.PositiveCFloat(1.0, allow_zero=False) def default_traits_view(self): base_view = Data2DPlotParams.default_traits_view(self) return View( Item('gridsize', editor=TextEditor(auto_set=False), label="Grid size"), Item('smoothed', label="Smooth"), Item('smoothed_sigma', editor=TextEditor(auto_set=False), label="Smooth\nsigma", visible_when="smoothed == True"), base_view.content)
class BasePlotParams(HasStrictTraits): title = Str xlabel = Str ylabel = Str huelabel = Str col_wrap = util.PositiveCInt(None, allow_zero = False, allow_none = True) sns_style = Enum(['whitegrid', 'darkgrid', 'white', 'dark', 'ticks']) sns_context = Enum(['paper', 'notebook', 'poster', 'talk']) legend = Bool(True) sharex = Bool(True) sharey = Bool(True) despine = Bool(True) def __repr__(self): return traits_repr(self)
class ImportPluginOp(PluginOpMixin, ImportOp): handler_factory = Callable(ImportHandler, transient=True) ret_events = util.PositiveInt(0, allow_zero=True, status=True) events = util.PositiveCInt(None, allow_zero=True, allow_none=True) def apply(self, experiment=None): ret = super().apply(experiment=experiment) self.ret_events = len(ret.data) return ret def get_notebook_code(self, idx): op = ImportOp() op.copy_traits(self, op.copyable_trait_names()) return dedent(""" op_{idx} = {repr} ex_{idx} = op_{idx}.apply()""".format(repr=repr(op), idx=idx))
class HistogramPlotParams(Data1DPlotParams): num_bins = util.PositiveCInt(None, allow_none = True) histtype = Enum(['stepfilled', 'step', 'bar']) linestyle = Enum(LINE_STYLES) linewidth = util.PositiveCFloat(None, allow_none = True, allow_zero = True) normed = Bool(False) def default_traits_view(self): base_view = Data1DPlotParams.default_traits_view(self) return View(Item('num_bins', editor = TextEditor(auto_set = False, format_func = lambda x: "" if x == None else str(x))), Item('histtype'), Item('linestyle'), Item('linewidth', editor = TextEditor(auto_set = False, format_func = lambda x: "" if x == None else str(x))), Item('normed'), base_view.content)
class PCAPluginOp(PluginOpMixin, PCAOp): handler_factory = Callable(PCAHandler) channels_list = List(_Channel, estimate = True) channels = List(Str, transient = True) scale = Dict(Str, util.ScaleEnum, transient = True) by = List(Str, estimate = True) num_components = util.PositiveCInt(2, allow_zero = False, estimate = True) whiten = Bool(False, estimate = True) @on_trait_change('channels_list_items, channels_list.+', post_init = True) def _channels_changed(self, obj, name, old, new): self.changed = (Changed.ESTIMATE, ('channels_list', self.channels_list)) # bits to support the subset editor subset_list = List(ISubset, estimate = True) subset = Property(Str, depends_on = "subset_list.str") # MAGIC - returns the value of the "subset" Property, above def _get_subset(self): return " and ".join([subset.str for subset in self.subset_list if subset.str]) @on_trait_change('subset_list.str') def _subset_changed(self, obj, name, old, new): self.changed = (Changed.ESTIMATE, ('subset_list', self.subset_list)) def estimate(self, experiment): for i, channel_i in enumerate(self.channels_list): for j, channel_j in enumerate(self.channels_list): if channel_i.channel == channel_j.channel and i != j: raise util.CytoflowOpError("Channel {0} is included more than once" .format(channel_i.channel)) self.channels = [] self.scale = {} for channel in self.channels_list: self.channels.append(channel.channel) self.scale[channel.channel] = channel.scale super().estimate(experiment, subset = self.subset) self.changed = (Changed.ESTIMATE_RESULT, self) def apply(self, experiment): for i, channel_i in enumerate(self.channels_list): for j, channel_j in enumerate(self.channels_list): if channel_i.channel == channel_j.channel and i != j: raise util.CytoflowOpError("Channel {0} is included more than once" .format(channel_i.channel)) self.channels = [] self.scale = {} for channel in self.channels_list: self.channels.append(channel.channel) self.scale[channel.channel] = channel.scale return super().apply(experiment) def clear_estimate(self): self._pca.clear() self.changed = (Changed.ESTIMATE_RESULT, self) def get_notebook_code(self, idx): op = PCAOp() op.copy_traits(self, op.copyable_trait_names()) for channel in self.channels_list: op.channels.append(channel.channel) op.scale[channel.channel] = channel.scale return dedent(""" op_{idx} = {repr} op_{idx}.estimate(ex_{prev_idx}{subset}) ex_{idx} = op_{idx}.apply(ex_{prev_idx}) """ .format(repr = repr(op), idx = idx, prev_idx = idx - 1, subset = ", subset = " + repr(self.subset) if self.subset else ""))
class KMeansWorkflowOp(WorkflowOperation, KMeansOp): # add "apply", "estimate" metadata name = Str(apply=True) xchannel = Str(estimate=True) ychannel = Str(estimate=True) xscale = util.ScaleEnum(estimate=True) yscale = util.ScaleEnum(estimate=True) num_clusters = util.PositiveCInt(2, allow_zero=False, estimate=True) by = List(Str, estimate=True) # add the 'estimate_result' metadata _kmeans = Dict(Any, Instance(sklearn.cluster.MiniBatchKMeans), transient=True, estimate_result=True) # override the base class's "subset" with one that is dynamically generated / # updated from subset_list subset = Property(Str, observe="subset_list.items.str") subset_list = List(ISubset, estimate=True) # bits to support the subset editor @observe('subset_list:items.str') def _on_subset_changed(self, _): self.changed = 'subset_list' # MAGIC - returns the value of the "subset" Property, above def _get_subset(self): return " and ".join( [subset.str for subset in self.subset_list if subset.str]) def default_view(self, **kwargs): return KMeansWorkflowView(op=self, **kwargs) def estimate(self, experiment): if not self.xchannel: raise util.CytoflowOpError('xchannel', "Must set X channel") if not self.ychannel: raise util.CytoflowOpError('ychannel', "Must set Y channel") self.channels = [self.xchannel, self.ychannel] self.scale = {self.xchannel: self.xscale, self.ychannel: self.yscale} super().estimate(experiment, subset=self.subset) def apply(self, experiment): if not self._kmeans: raise util.CytoflowOpError(None, 'Click "Estimate"!') return KMeansOp.apply(self, experiment) def clear_estimate(self): self._kmeans = {} self._scale = {} def get_notebook_code(self, idx): op = KMeansOp() op.copy_traits(self, op.copyable_trait_names()) op.channels = [self.xchannel, self.ychannel] op.scale = {self.xchannel: self.xscale, self.ychannel: self.yscale} return dedent(""" op_{idx} = {repr} op_{idx}.estimate(ex_{prev_idx}{subset}) ex_{idx} = op_{idx}.apply(ex_{prev_idx}) """.format(repr=repr(op), idx=idx, prev_idx=idx - 1, subset=", subset = " + repr(self.subset) if self.subset else ""))
class DensityPlotParams(Data2DPlotParams): gridsize = util.PositiveCInt(50, allow_zero=False) smoothed = Bool(False) smoothed_sigma = util.PositiveCFloat(1.0, allow_zero=False)
class GaussianMixture1DPluginOp(PluginOpMixin, GaussianMixtureOp): id = Constant('edu.mit.synbio.cytoflowgui.operations.gaussian_1d') handler_factory = Callable(GaussianMixture1DHandler) channel = Str channel_scale = util.ScaleEnum(estimate=True) # add "estimate" metadata num_components = util.PositiveCInt(1, estimate=True) sigma = util.PositiveCFloat(0.0, allow_zero=True, estimate=True) by = List(Str, estimate=True) # bits to support the subset editor subset_list = List(ISubset, estimate=True) subset = Property(Str, depends_on="subset_list.str") # MAGIC - returns the value of the "subset" Property, above def _get_subset(self): return " and ".join( [subset.str for subset in self.subset_list if subset.str]) @on_trait_change('subset_list.str') def _subset_changed(self, obj, name, old, new): self.changed = (Changed.ESTIMATE, ('subset_list', self.subset_list)) _gmms = Dict(Any, Instance(mixture.GaussianMixture), transient=True) @on_trait_change('channel') def _channel_changed(self): self.channels = [self.channel] self.changed = (Changed.ESTIMATE, ('channels', self.channels)) if self.channel_scale: self.scale = {self.channel: self.channel_scale} self.changed = (Changed.ESTIMATE, ('scale', self.scale)) @on_trait_change('channel_scale') def _scale_changed(self): if self.channel: self.scale = {self.channel: self.channel_scale} self.changed = (Changed.ESTIMATE, ('scale', self.scale)) def estimate(self, experiment): super().estimate(experiment, subset=self.subset) self.changed = (Changed.ESTIMATE_RESULT, self) def default_view(self, **kwargs): return GaussianMixture1DPluginView(op=self, **kwargs) def clear_estimate(self): self._gmms = {} self._scale = {} self.changed = (Changed.ESTIMATE_RESULT, self) def get_notebook_code(self, idx): op = GaussianMixtureOp() op.copy_traits(self, op.copyable_trait_names()) return dedent(""" op_{idx} = {repr} op_{idx}.estimate(ex_{prev_idx}{subset}) ex_{idx} = op_{idx}.apply(ex_{prev_idx}) """.format(repr=repr(op), idx=idx, prev_idx=idx - 1, subset=", subset = " + repr(self.subset) if self.subset else ""))
class GaussianMixture1DWorkflowOp(WorkflowOperation, GaussianMixtureOp): # override id so we can differentiate the 1D and 2D ops id = Constant('edu.mit.synbio.cytoflowgui.operations.gaussian_1d') # add 'estimate' and 'apply' metadata name = Str(apply=True) channel = Str(estimate=True) channel_scale = util.ScaleEnum(estimate=True) num_components = util.PositiveCInt(1, allow_zero=False, estimate=True) sigma = util.PositiveCFloat(None, allow_zero=True, allow_none=True, estimate=True) by = List(Str, estimate=True) # add the 'estimate_result' metadata _gmms = Dict(Any, Instance(mixture.GaussianMixture), transient=True, estimate_result=True) # override the base class's "subset" with one that is dynamically generated / # updated from subset_list subset = Property(Str, observe="subset_list.items.str") subset_list = List(ISubset, estimate=True) # bits to support the subset editor @observe('subset_list:items.str') def _on_subset_changed(self, _): self.changed = 'subset_list' # MAGIC - returns the value of the "subset" Property, above def _get_subset(self): return " and ".join( [subset.str for subset in self.subset_list if subset.str]) def estimate(self, experiment): self.channels = [self.channel] self.scale = {self.channel: self.channel_scale} super().estimate(experiment, subset=self.subset) def apply(self, experiment): if not self._gmms: raise util.CytoflowOpError(None, 'Click "Estimate"!') return GaussianMixtureOp.apply(self, experiment) def default_view(self, **kwargs): return GaussianMixture1DWorkflowView(op=self, **kwargs) def clear_estimate(self): self._gmms = {} self._scale = {} def get_notebook_code(self, idx): op = GaussianMixtureOp() op.copy_traits(self, op.copyable_trait_names()) op.channels = [self.channel] op.scale = {self.channel: self.channel_scale} return dedent(""" op_{idx} = {repr} op_{idx}.estimate(ex_{prev_idx}{subset}) ex_{idx} = op_{idx}.apply(ex_{prev_idx}) """.format(repr=repr(op), idx=idx, prev_idx=idx - 1, subset=", subset = " + repr(self.subset) if self.subset else ""))
class KMeansPluginOp(PluginOpMixin, KMeansOp): handler_factory = Callable(FlowPeaksHandler) # add "estimate" metadata xchannel = Str(estimate=True) ychannel = Str(estimate=True) xscale = util.ScaleEnum(estimate=True) yscale = util.ScaleEnum(estimate=True) num_clusters = util.PositiveCInt(2, allow_zero=False, estimate=True) by = List(Str, estimate=True) # bits to support the subset editor subset_list = List(ISubset, estimate=True) subset = Property(Str, depends_on="subset_list.str") # MAGIC - returns the value of the "subset" Property, above def _get_subset(self): return " and ".join( [subset.str for subset in self.subset_list if subset.str]) @on_trait_change('subset_list.str') def _subset_changed(self, obj, name, old, new): self.changed = (Changed.ESTIMATE, ('subset_list', self.subset_list)) @on_trait_change('xchannel, ychannel') def _channel_changed(self, obj, name, old, new): self.channels = [] self.scale = {} if self.xchannel: self.channels.append(self.xchannel) if self.xchannel in self.scale: del self.scale[self.xchannel] self.scale[self.xchannel] = self.xscale if self.ychannel: self.channels.append(self.ychannel) if self.ychannel in self.scale: del self.scale[self.ychannel] self.scale[self.ychannel] = self.yscale @on_trait_change('xscale, yscale') def _scale_changed(self, obj, name, old, new): self.scale = {} if self.xchannel: self.scale[self.xchannel] = self.xscale if self.ychannel: self.scale[self.ychannel] = self.yscale def default_view(self, **kwargs): return KMeansPluginView(op=self, **kwargs) def estimate(self, experiment): if not self.xchannel: raise util.CytoflowOpError('xchannel', "Must set X channel") if not self.ychannel: raise util.CytoflowOpError('ychannel', "Must set Y channel") try: super().estimate(experiment, subset=self.subset) except: raise finally: self.changed = (Changed.ESTIMATE_RESULT, self) def clear_estimate(self): self._kmeans.clear() self.changed = (Changed.ESTIMATE_RESULT, self) def get_notebook_code(self, idx): op = KMeansOp() op.copy_traits(self, op.copyable_trait_names()) return dedent(""" op_{idx} = {repr} op_{idx}.estimate(ex_{prev_idx}{subset}) ex_{idx} = op_{idx}.apply(ex_{prev_idx}) """.format(repr=repr(op), idx=idx, prev_idx=idx - 1, subset=", subset = " + repr(self.subset) if self.subset else ""))
class PCAWorkflowOp(WorkflowOperation, PCAOp): # use a list of _Channel instead of separate lists/dicts of channels/scales channels_list = List(Channel, estimate=True) channels = Property( List(Str), observe= '[channels_list.items,channels_list.items.channel,channels_list.items.scale]' ) scale = Property( Dict(Str, util.ScaleEnum), observe= '[channels_list.items,channels_list.items.channel,channels_list.items.scale]' ) # add 'estimate', 'apply' metadata name = Str(apply=True) by = List(Str, estimate=True) num_components = util.PositiveCInt(2, allow_zero=False, estimate=True) whiten = Bool(False, estimate=True) # add the 'estimate_result' metadata _pca = Dict(Any, Any, estimate_result=True, transient=True) # override the base class's "subset" with one that is dynamically generated / # updated from subset_list subset = Property(Str, observe="subset_list.items.str") subset_list = List(ISubset, estimate=True) # bits for channels @observe( '[channels_list:items,channels_list:items.channel,channels_list:items.scale]' ) def _channels_updated(self, event): self.changed = 'channels_list' def _get_channels(self): return [c.channel for c in self.channels_list] def _get_scale(self): return {c.channel: c.scale for c in self.channels_list} # bits to support the subset editor @observe('subset_list:items.str') def _on_subset_changed(self, _): self.changed = 'subset_list' # MAGIC - returns the value of the "subset" Property, above def _get_subset(self): return " and ".join( [subset.str for subset in self.subset_list if subset.str]) def estimate(self, experiment): for i, channel_i in enumerate(self.channels_list): for j, channel_j in enumerate(self.channels_list): if channel_i.channel == channel_j.channel and i != j: raise util.CytoflowOpError( "Channel {0} is included more than once".format( channel_i.channel)) super().estimate(experiment, subset=self.subset) def apply(self, experiment): if not self._pca: raise util.CytoflowOpError(None, 'Click "Estimate"!') return super().apply(experiment) def clear_estimate(self): self._pca = {} self._scale = {} def get_notebook_code(self, idx): op = PCAOp() op.copy_traits(self, op.copyable_trait_names()) return dedent(""" op_{idx} = {repr} op_{idx}.estimate(ex_{prev_idx}{subset}) ex_{idx} = op_{idx}.apply(ex_{prev_idx}) """.format(repr=repr(op), idx=idx, prev_idx=idx - 1, subset=", subset = " + repr(self.subset) if self.subset else ""))