Source code for cytoflowgui.workflow.subset

#!/usr/bin/env python3.8
# coding: latin-1

# (c) Massachusetts Institute of Technology 2015-2018
# (c) Brian Teague 2018-2022
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""
cytoflowgui.workflow.subset
---------------------------

Structured representation of the possible clauses for a **subset** 
argument to `IOperation.estimate` or `IView.plot`.  

`ISubset` - The top-level `traits.has_traits.Interface`

`BoolSubset` - represents a subset of True or False values from a boolean 
    condition

`CategorySubset` - represents a subset of one or more values from a categorical
    condition.
    
`RangeSubset` - represents a subset that is a range of values from a 
    numerical condition.
"""

from traits.api import (provides, Interface, Str, List, Property, Bool,
                        HasStrictTraits, CFloat, Undefined, observe)

from cytoflow import utility as util

from .serialization import camel_registry, traits_repr

[docs]class ISubset(Interface): """The interface that the rest of the subset classes must implement.""" name = Str values = List str = Property(Str)
[docs]@provides(ISubset) class BoolSubset(HasStrictTraits): """A subset that selects either True or False values from a boolean condition""" name = Str values = List # unused selected_t = Bool(False) selected_f = Bool(False) str = Property(Str, observe = "[name,selected_t,selected_f]") def _get_str(self): if self.selected_t and not self.selected_f: return "({0} == True)".format(util.sanitize_identifier(self.name)) elif not self.selected_t and self.selected_f: return "({0} == False)".format(util.sanitize_identifier(self.name)) else: return "" def __eq__(self, other): return (self.name == other.name and self.values == other.values and self.selected_t == other.selected_t and self.selected_f == other.selected_f) def __hash__(self): return hash((self.name, tuple(self.values), self.selected_t, self.selected_f))
BoolSubset.__repr__ = traits_repr @camel_registry.dumper(BoolSubset, 'bool-subset', 1) def _dump_bool_subset(bs): return dict(name = bs.name, values = bs.values, selected_t = bs.selected_t, selected_f = bs.selected_f) @camel_registry.loader('bool-subset', 1) def _load_bool_subset(data, version): return BoolSubset(**data)
[docs]@provides(ISubset) class CategorySubset(HasStrictTraits): """A subset that selects one or more values from a categorical condition""" name = Str values = List selected = List str = Property(Str, observe = '[name,selected.items]') # MAGIC: gets the value of the Property trait "subset_str" def _get_str(self): if len(self.selected) == 0: return "" phrase = "(" for cat in self.selected: if len(phrase) > 1: phrase += " or " phrase += "{0} == \"{1}\"".format(util.sanitize_identifier(self.name), cat) phrase += ")" return phrase def __eq__(self, other): return (self.name == other.name and self.values == other.values and self.selected == other.selected) def __hash__(self): return hash((self.name, tuple(self.values), tuple(self.selected)))
CategorySubset.__repr__ = traits_repr @camel_registry.dumper(CategorySubset, 'category-subset', 1) def _dump_category_subset(cs): return dict(name = cs.name, values = cs.values, selected = cs.selected) @camel_registry.loader('category-subset', 1) def _load_category_subset(data, version): return CategorySubset(**data)
[docs]@provides(ISubset) class RangeSubset(HasStrictTraits): """A subset that selects a range from a numerical condition""" name = Str values = List high = CFloat(Undefined) low = CFloat(Undefined) str = Property(Str, observe = "[name,values,high,low]") # MAGIC: gets the value of the Property trait "subset_str" def _get_str(self): if self.low == self.values[0] and self.high == self.values[-1]: return "" elif self.low == self.high: return "({0} == {1})" \ .format(util.sanitize_identifier(self.name), self.low) else: return "({0} >= {1} and {0} <= {2})" \ .format(util.sanitize_identifier(self.name), self.low, self.high) @observe('[values,values.items]') def _values_changed(self, _): if self.high is Undefined: self.high = max(self.values) if self.low is Undefined: self.low = min(self.values) def __eq__(self, other): return (self.name == other.name and self.values == other.values and self.low == other.low and self.high == other.high) def __hash__(self): return hash((self.name, tuple(self.values), self.low, self.high))
RangeSubset.__repr__ = traits_repr @camel_registry.dumper(RangeSubset, 'range-subset', 1) def _dump_range_subset(rs): return dict(name = rs.name, values = rs.values, high = rs.high, low = rs.low) @camel_registry.loader('range-subset', 1) def _load_range_subset(data, version): return RangeSubset(**data)