class PivotTable(PlotObject): title = String("Pivot Table") description = String("") source = Instance(has_ref=True) data = Dict() fields = List() # List[{name: String, dtype: String}] rows = List() columns = List() values = List() filters = List() manual_update = Bool(True) def setup_events(self): self.on_change('rows', self, 'get_data') self.on_change('columns', self, 'get_data') self.on_change('values', self, 'get_data') self.on_change('filters', self, 'get_data') if not self.fields: self.fields = self.source.fields() if not self.data: self.get_data() def get_data(self, obj=None, attrname=None, old=None, new=None): self.data = self.source.pivot( dict( rows=self.rows, columns=self.columns, values=self.values, filters=self.filters, ))
class CollisionModifier(HasProps): """Models an special type of operation that alters how glyphs interact. Used to handle the manipulation of glyphs for operations, such as stacking. The list of `CompositeGlyph`s can either be input into the `CollisionModifier` as keyword args, or added individually with the `add_glyph` method. """ comp_glyphs = List(Instance(CompositeGlyph), help="""A list of composite glyphs, to apply the modification to.""") name = String(help="""The name of the collision modifier.""") method_name = String( help="""The name of the method that will be utilized on the composite glyphs. This method must exist on all `comp_glyphs`.""") columns = Either(ColumnLabel, List(ColumnLabel), help="""Some collision modifiers might require column labels to apply the operation in relation to.""") def add_glyph(self, comp_glyph): self.comp_glyphs.append(comp_glyph) def apply(self, renderers=None): if len(self.comp_glyphs) == 0: self.comp_glyphs = renderers if len(self.comp_glyphs) > 0: # the first renderer's operation method is applied to the rest getattr(self.comp_glyphs[0], self.method_name)(self.comp_glyphs) else: raise AttributeError( '%s must be applied to available renderers, none found.' % self.__class__.__name__)
class Workspace(PlotObject): varname = String() data_table = Instance(DataTable, has_ref=True) pivot_tables = List(Instance(PivotTable, has_ref=True), has_ref=True) plots = List(Instance(Plot, has_ref=True), has_ref=True) plot_context = Instance(PlotContext, has_ref=True) active_tab = Int(0)
class DataSource(PlotObject): """ Base class for data sources """ # List of names of the fields of each tuple in self.data # ordering is incoporated here column_names = List() selected = List() #index of selected points def columns(self, *columns): """ Returns a ColumnsRef object that points to a column or set of columns on this data source """ return ColumnsRef(source=self, columns=list(columns))
class ObjectArrayDataSource(DataSource): # List of tuples of values data = List() # List of names of the fields of each tuple in self.data column_names = List() # Maps field/column name to a DataRange or FactorRange object. If the # field is not in the dict, then a range is created automatically. cont_ranges = Dict() discrete_ranges = Dict()
class DataTable(PlotObject): source = Instance(has_ref=True) sort = List() group = List() offset = Int(default=0) length = Int(default=100) maxlength = Int() totallength = Int() tabledata = Dict() filterselected = Bool(default=False) def setup_events(self): self.on_change('sort', self, 'get_data') self.on_change('group', self, 'get_data') self.on_change('length', self, 'get_data') self.on_change('offset', self, 'get_data') self.on_change('filterselected', self, 'get_data') self.source.on_change('selected', self, 'get_data') self.source.on_change('data', self, 'get_data') self.source.on_change('computed_columns', self, 'get_data') if not self.tabledata: self.get_data() def transform(self): return dict( sort=self.sort, group=self.group, offset=self.offset, length=self.length, filterselected=self.filterselected, ) def setselect(self, select): self.source.setselect(select, self.transform()) self.get_data() def select(self, select): self.source.select(select, self.transform()) self.get_data() def deselect(self, deselect): self.source.deselect(deselect, self.transform()) self.get_data() def get_data(self, obj=None, attrname=None, old=None, new=None): data = self.source.get_data(self.transform()) self.maxlength = data.pop('maxlength') self.totallength = data.pop('totallength') self.tabledata = data
def test_Tuple(self): with self.assertRaises(TypeError): prop = Tuple() with self.assertRaises(TypeError): prop = Tuple(Int) prop = Tuple(Int, String, List(Int)) self.assertTrue(prop.is_valid(None)) self.assertFalse(prop.is_valid(False)) self.assertFalse(prop.is_valid(True)) self.assertFalse(prop.is_valid(0)) self.assertFalse(prop.is_valid(1)) self.assertFalse(prop.is_valid(0.0)) self.assertFalse(prop.is_valid(1.0)) self.assertFalse(prop.is_valid(1.0 + 1.0j)) self.assertFalse(prop.is_valid("")) self.assertFalse(prop.is_valid(())) self.assertFalse(prop.is_valid([])) self.assertFalse(prop.is_valid({})) self.assertFalse(prop.is_valid(Foo())) self.assertTrue(prop.is_valid((1, "", [1, 2, 3]))) self.assertFalse(prop.is_valid((1.0, "", [1, 2, 3]))) self.assertFalse(prop.is_valid((1, True, [1, 2, 3]))) self.assertFalse(prop.is_valid((1, "", (1, 2, 3)))) self.assertFalse(prop.is_valid((1, "", [1, 2, "xyz"])))
def test_Either(self): with self.assertRaises(TypeError): prop = Either() prop = Either(Range(Int, 0, 100), Regex("^x*$"), List(Int)) self.assertTrue(prop.is_valid(None)) # TODO: self.assertFalse(prop.is_valid(False)) # TODO: self.assertFalse(prop.is_valid(True)) self.assertTrue(prop.is_valid(0)) self.assertTrue(prop.is_valid(1)) self.assertFalse(prop.is_valid(0.0)) self.assertFalse(prop.is_valid(1.0)) self.assertFalse(prop.is_valid(1.0 + 1.0j)) self.assertTrue(prop.is_valid("")) self.assertFalse(prop.is_valid(())) self.assertTrue(prop.is_valid([])) self.assertFalse(prop.is_valid({})) self.assertFalse(prop.is_valid(Foo())) self.assertTrue(prop.is_valid(100)) self.assertFalse(prop.is_valid(-100)) self.assertTrue(prop.is_valid("xxx")) self.assertFalse(prop.is_valid("yyy")) self.assertTrue(prop.is_valid([1, 2, 3])) self.assertFalse(prop.is_valid([1, 2, ""]))
class NestedCompositeGlyph(CompositeGlyph): """A composite glyph that consists of other composite glyphs. An important responsibility of any `CompositeGlyph` is to understand the bounds of the glyph renderers that make it up. This class is used to provide convenient properties that return the bounds from the child `CompositeGlyphs`. """ children = List(Instance(CompositeGlyph)) @property def y_max(self): return max([renderer.y_max for renderer in self.children]) @property def y_min(self): return min([renderer.y_min for renderer in self.children]) @property def x_min(self): return min([renderer.x_min for renderer in self.children]) @property def x_max(self): return max([renderer.x_max for renderer in self.children])
class GridPlot(PlotObject): """ A 2D grid of plots """ __view_model__ = "GridPlotContainer" children = List(List) border_space = Int(0)
class DashAttr(AttrSpec): name = 'dash' iterable = List(String, default=dashes) def __init__(self, **kwargs): iterable = kwargs.pop('dash', None) if iterable is not None: kwargs['iterable'] = iterable super(DashAttr, self).__init__(**kwargs)
class MarkerAttr(AttrSpec): name = 'marker' iterable = List(String, default=list(marker_types.keys())) def __init__(self, **kwargs): iterable = kwargs.pop('markers', None) if iterable is not None: kwargs['iterable'] = iterable super(MarkerAttr, self).__init__(**kwargs)
class ColorAttr(AttrSpec): name = 'color' iterable = List(Color, default=DEFAULT_PALETTE) def __init__(self, **kwargs): iterable = kwargs.pop('palette', None) if iterable is not None: kwargs['iterable'] = iterable super(ColorAttr, self).__init__(**kwargs)
class CDX(PlotObject): namespace = Instance(Namespace, has_ref=True) workspaces = List(Instance(Workspace, has_ref=True), has_ref=True) active_workspace = Instance(Workspace, has_ref=True) activeplot = Instance(Plot, has_ref=True) plotlist = Instance(PlotList, has_ref=True) plotcontext = Instance(PlotContext, has_ref=True) # list of to level UI elems
class MarkerAttr(AttrSpec): """An attribute specification for mapping unique data values to markers.""" name = 'marker' iterable = List(String, default=list(marker_types.keys())) def __init__(self, **kwargs): iterable = kwargs.pop('markers', None) if iterable is not None: kwargs['iterable'] = iterable super(MarkerAttr, self).__init__(**kwargs)
def test_List(self): with self.assertRaises(TypeError): prop = List() prop = List(Int) self.assertTrue(prop.is_valid(None)) self.assertFalse(prop.is_valid(False)) self.assertFalse(prop.is_valid(True)) self.assertFalse(prop.is_valid(0)) self.assertFalse(prop.is_valid(1)) self.assertFalse(prop.is_valid(0.0)) self.assertFalse(prop.is_valid(1.0)) self.assertFalse(prop.is_valid(1.0 + 1.0j)) self.assertFalse(prop.is_valid("")) self.assertFalse(prop.is_valid(())) self.assertTrue(prop.is_valid([])) self.assertFalse(prop.is_valid({})) self.assertFalse(prop.is_valid(Foo()))
class DashAttr(AttrSpec): """An attribute specification for mapping unique data values to line dashes.""" name = 'dash' iterable = List(String, default=dashes) def __init__(self, **kwargs): iterable = kwargs.pop('dash', None) if iterable is not None: kwargs['iterable'] = iterable super(DashAttr, self).__init__(**kwargs)
class Bins(Stat): """A set of many individual Bin stats. Bin counts using: https://en.wikipedia.org/wiki/Freedman%E2%80%93Diaconis_rule """ bin_count = Either(Int, Float) bin_width = Float(default=None, help='Use Freedman-Diaconis rule if None.') bins = List(Instance(Bin)) q1 = Quantile(interval=0.25) q3 = Quantile(interval=0.75) labels = List(String) def __init__(self, values=None, column=None, bins=None, **properties): properties['values'] = values properties['column'] = column properties['bins'] = bins super(Bins, self).__init__(**properties) def update(self): values = self.get_data() self.q1.set_data(values) self.q3.set_data(values) if self.bin_count is None: self.calc_num_bins(values) def calculate(self): binned, bin_edges = pd.cut(self.get_data(), self.bin_count, retbins=True, precision=0) df = pd.DataFrame(dict(values=self.get_data(), bins=binned)) bins = [] for name, group in df.groupby('bins'): bins.append(Bin(bin_label=name, values=group['values'])) self.bins = bins def calc_num_bins(self, values): iqr = self.q3.value - self.q1.value self.bin_width = 2 * iqr * (len(values)**-(1. / 3.)) self.bin_count = np.ceil( (self.values.max() - self.values.min()) / self.bin_width)
class Plot(PlotObject): data_sources = List title = String("Bokeh Plot") x_range = Instance(DataRange1d, has_ref=True) y_range = Instance(DataRange1d, has_ref=True) # We shouldn't need to create mappers manually on the Python side #xmapper = Instance(LinearMapper) #ymapper = Instance(LinearMapper) #mapper = Instance(GridMapper) # A list of all renderers on this plot; this includes guides as well # as glyph renderers renderers = List(has_ref=True) tools = List(has_ref=True) # TODO: These don't appear in the CS source, but are created by mpl.py, so # I'm leaving them here for initial compatibility testing. axes = List(has_ref=True) # TODO: How do we want to handle syncing of the different layers? # image = List # underlay = List # glyph = List # overlay = List # annotation = List height = Int(400) width = Int(400) background_fill = Color("white") border_fill = Color("white") canvas_width = Int(400) canvas_height = Int(400) outer_width = Int(400) outer_height = Int(400) border_top = Int(50) border_bottom = Int(50) border_left = Int(50) border_right = Int(50)
class CollisionModifier(HasProps): renderers = List(Instance(CompositeGlyph)) name = String() method_name = String() columns = Either(ColumnLabel, List(ColumnLabel)) def add_renderer(self, renderer): self.renderers.append(renderer) def apply(self, renderers=None): if len(self.renderers) == 0: self.renderers = renderers if len(self.renderers) > 0: # the first renderer's operation method is applied to the rest getattr(self.renderers[0], self.method_name)(self.renderers) else: raise AttributeError( '%s must be applied to available renderers, none found.' % self.__class__.__name__)
class ColorAttr(AttrSpec): """An attribute specification for mapping unique data values to colors. .. note:: Should be expanded to support more complex coloring options. """ name = 'color' iterable = List(Color, default=DEFAULT_PALETTE) def __init__(self, **kwargs): iterable = kwargs.pop('palette', None) if iterable is not None: kwargs['iterable'] = iterable super(ColorAttr, self).__init__(**kwargs)
class DataRange(PlotObject): sources = List(ColumnsRef, has_ref=True) def vm_serialize(self): props = self.vm_props(withvalues=True) props['id'] = self._id sources = props.pop("sources") props["sources"] = [{"ref":cr.source, "columns":cr.columns} for cr in sources] return props def finalize(self, models): super(DataRange, self).finalize(models) for idx, source in enumerate(self.sources): if isinstance(source, dict): self.sources[idx] = ColumnsRef( source=source['ref'], columns=source['columns']) def references(self): return [x.source for x in self.sources]
class NestedCompositeGlyph(CompositeGlyph): """A composite glyph that consists of other composite glyphs.""" children = List(Instance(CompositeGlyph)) @property def y_max(self): return max([renderer.y_max for renderer in self.children]) @property def y_min(self): return min([renderer.y_min for renderer in self.children]) @property def x_min(self): return min([renderer.x_min for renderer in self.children]) @property def x_max(self): return max([renderer.x_max for renderer in self.children])
class RemoteDataSource(PlotObject): host = String("localhost") port = Int(10020) varname = String() computed_columns = List() metadata = Dict() #hack... we're just using this field right now to trigger events selected = Int(0) data = Int(0) # from IPython.kernel import KernelManager # kernel = KernelManager(connection_file="kernel-1.json") # kernel.load_connection_file() # client = kernel.client() # client.start_channels() # client.shell_channel.execute("x = 1", store_history=False) def _url(self, func=None): remotedata = self func = "/" + func if func is not None else "" url = "http://%s:%s/array/%s%s" % \ (remotedata.host, remotedata.port, remotedata.varname, func) return url def _is_ok(self, response): response.raise_for_status() def _trigger_events(self): self.selected = self.selected + 1 def setselect(self, select, transform): data = transform data['selected'] = select json = protocol.serialize_json(data) requests.post(self._url("setselect"), data=json) self._trigger_events() def search(self, search): requests.post(self._url("search"), data=search) self._trigger_events() def select(self, select, transform): data = transform data['selected'] = select json = protocol.serialize_json(data) requests.post(self._url("select"), data=json) self._trigger_events() def deselect(self, deselect, transform): data = transform data['selected'] = deselect requests.post(self._url("selected"), data=protocol.serialize_json(data)) self._trigger_events() def pivot(self, transform): json = protocol.serialize_json(transform) response = requests.post(self._url("pivot"), data=json) self._is_ok(response) data = response.json() self._trigger_events() return data def fields(self): json = protocol.serialize_json({}) response = requests.get(self._url("fields"), data=json) self._is_ok(response) data = response.json() self._trigger_events() return data def get_data(self, transform): json = protocol.serialize_json(transform) response = requests.get(self._url(), data=json) self._is_ok(response) data = response.json() self.metadata = data.pop('metadata', {}) return data def set_computed_columns(self, computed_columns): json = protocol.serialize_json(computed_columns) response = requests.get(self._url("computed"), data=json) self._is_ok(response) data = response.json() self.computed_columns = computed_columns self.data += 1 return data
class PlotContext(PlotObject): children = List(has_ref=True)
class Bar(PlotObject): """ This is a Bar model. """ thing = List(Int, help="doc for thing")
class IPythonRemoteData(PlotObject): host = String("localhost") port = Int(10020) varname = String() computed_columns = List() metadata = Dict() #hack... we're just using this field right now to trigger events selected = Int(0) data = Int(0) def setselect(self, select, transform): remotedata = self url = "http://%s:%s/array/%s/setselect" % ( remotedata.host, remotedata.port, remotedata.varname) data = transform data['selected'] = select requests.post(url, data=protocol.serialize_json(data)) self.selected = self.selected + 1 def search(self, search): remotedata = self url = "http://%s:%s/array/%s/search" % ( remotedata.host, remotedata.port, remotedata.varname) requests.post(url, data=search) self.selected = self.selected + 1 def select(self, select, transform): remotedata = self url = "http://%s:%s/array/%s/select" % ( remotedata.host, remotedata.port, remotedata.varname) data = transform data['selected'] = select requests.post(url, data=protocol.serialize_json(data)) self.selected = self.selected + 1 def deselect(self, deselect, transform): remotedata = self url = "http://%s:%s/array/%s/deselect" % ( remotedata.host, remotedata.port, remotedata.varname) data = transform data['selected'] = deselect requests.post(url, data=protocol.serialize_json(data)) self.selected = self.selected + 1 def get_data(self, transform): remotedata = self url = "http://%s:%s/array/%s" % (remotedata.host, remotedata.port, remotedata.varname) data = requests.get(url, data=protocol.serialize_json(transform)).json() self.metadata = data.pop('metadata', {}) return data def set_computed_columns(self, computed_columns): remotedata = self url = "http://%s:%s/array/%s/computed" % ( remotedata.host, remotedata.port, remotedata.varname) data = requests.get( url, data=protocol.serialize_json(computed_columns)).json() self.computed_columns = computed_columns self.data += 1 return data
class PandasPivotTable(PlotObject): source = Instance(has_ref=True) sort = List() group = List() offset = Int(default=0) length = Int(default=100) maxlength = Int() totallength = Int() precision = Dict() tabledata = Dict() filterselected = Bool(default=False) def setup_events(self): self.on_change('sort', self, 'get_data') self.on_change('group', self, 'get_data') self.on_change('length', self, 'get_data') self.on_change('offset', self, 'get_data') self.on_change('precision', self, 'get_data') self.on_change('filterselected', self, 'get_data') self.source.on_change('selected', self, 'get_data') self.source.on_change('data', self, 'get_data') self.source.on_change('computed_columns', self, 'get_data') if not self.tabledata: self.get_data() def format_data(self, jsondata): """inplace manipulation of jsondata """ precision = self.precision for colname, data in jsondata.iteritems(): if colname == '_selected' or colname == '_counts': continue if self.source.metadata.get(colname, {}).get('date'): isdate = True else: isdate = False for idx, val in enumerate(data): if isdate: timeobj = time.localtime(val / 1000.0) data[idx] = time.strftime("%Y-%m-%d %H:%M:%S", timeobj) if isinstance(val, float): data[idx] = "%%.%df" % precision.get(colname, 2) % data[idx] def transform(self): return dict( sort=self.sort, group=self.group, offset=self.offset, length=self.length, filterselected=self.filterselected, ) def setselect(self, select): self.source.setselect(select, self.transform()) self.get_data() def select(self, select): self.source.select(select, self.transform()) self.get_data() def deselect(self, deselect): self.source.deselect(deselect, self.transform()) self.get_data() def get_data(self, obj=None, attrname=None, old=None, new=None): data = self.source.get_data(self.transform()) print data['data']['_selected'] self.maxlength = data.pop('maxlength') self.totallength = data.pop('totallength') self.format_data(data['data']) self.tabledata = data
class HasListProp(PlotObject): foo = List(String) def __init__(self, **kwargs): super(HasListProp, self).__init__(**kwargs)
class V(self.pObjectClass): u1 = Instance(U) u2 = List(Instance(U)) u3 = Tuple(Int, Instance(U)) u4 = Dict(String, Instance(U)) u5 = Dict(String, List(Instance(U)))