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 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 DatetimeAxis(LinearAxis): type = String("datetime_axis") axis_label = String("date") scale = String("time") num_labels = Int(8) char_width = Int(10) fill_ratio = Float(0.3) formats = Dict({"days": ["%m/%d/%Y"]})
class Grid(GuideRenderer): """ 1D Grid component """ type = String("grid") dimension = Int(0) bounds = String('auto') # Line props grid_props = Include(LineProps, prefix="grid")
class Blend(DataOperator): """Creates combined variables, increasing the number of distinct items. The primary action that blend is taking is combining and collapsing multiple columns together, using set algebra. You can think of this like taking two columns with similar or different data and stacking them on top of each other. The new categories are the union of the two sets. The operation is like an OR because a category in either variable is included in the blended variable .. note:: The variables not being blended must be duplicated (consider a sample time). For example, two variables, 'sensor_a' and 'sensor_b' only contain two values, either 'on' or 'off', with one more column of 'datetime'. Blending 'sensor_a' and 'sensor_b' results in two columns, 'datetime' and 'sensors_state'. Example: - cat1 + cat2 = [cat1, cat2] - cat1 + num1 = [cat1, cat(num1)] - num1 + num2 = [num1, num2] Can be used to stack column oriented measures, so they can be colored. In this case, a new categorical column will be created that identifies each measure by name of the previous column. Can be used to combine categorical columns. ToDo: address booleans. Consider two columns, 'was_successful', 'was_effective'. It seems like blending of booleans would be performing an *or*. See Grammar of Graphics pgs. 67, 320 """ name = String(default='value', help="""The name of the column to contain the values of the blended columns.""") labels_name = String(default='variable', help="""The name of the column to contain the names of the columns that were blended.""" ) def __init__(self, *cols, **properties): properties['columns'] = list(cols) super(Blend, self).__init__(**properties) def apply(self, data): data_copy = copy(data) data_copy.stack_measures(measures=self.columns, value_name=self.name, var_name=self.labels_name) return data_copy._data
class GuideRenderer(PlotObject): plot = Instance dimension = Int(0) location = String('min') bounds = String('auto') def __init__(self, **kwargs): super(GuideRenderer, self).__init__(**kwargs) if self.plot is not None: if self not in self.plot.renderers: self.plot.renderers.append(self) def vm_serialize(self): props = self.vm_props(withvalues=True) del props["plot"] return {"id": self._id, "plot": self.plot, "guidespec": props}
class XyGlyph(CompositeGlyph): """Composite glyph that plots in cartesian coordinates.""" x = EitherColumn(String, Column(Float), Column(String), Column(Datetime), Column(Bool)) y = EitherColumn(String, Column(Float), Column(String), Column(Datetime), Column(Bool)) line_color = String(default=DEFAULT_PALETTE[0]) line_alpha = Float(default=1.0) def build_source(self): if self.x is None: x = [self.label] * len(self.y) data = dict(x_values=x, y_values=self.y) elif self.y is None: y = [self.label] * len(self.x) data = dict(x_values=self.x, y_values=y) else: data = dict(x_values=self.x, y_values=self.y) return ColumnDataSource(data) @property def x_max(self): return max(self.source._data['x_values']) @property def x_min(self): return min(self.source._data['x_values']) @property def y_max(self): return max(self.source._data['y_values']) @property def y_min(self): return min(self.source._data['y_values'])
class StyleableBox(BaseBox): ''' styleable box provides element level css_properties as a dictionary ''' __implementation__ = load_component('./styleable_box.coffee') css_properties = Dict(String, Any, default=None) orientation = String(default='vertical')
class MyModel(VBoxModelForm): """Input Widgets, define the fields you want to read from the input here as bokeh properties input_specs is a list of dictionary, specifying how the kind of input widget you want for each property. the name field must match one of the properties, for example here, we use names of offset and scale. You can also specify title, if you want a different label in the generated form """ offset = Float(1.0) scale = Float(1.0) title = String(default="my sin wave") input_specs = [ { "widget": TextInput, "name": "title", "value": "my sin wave" }, { "widget": Slider, "name": "offset", "value": 1.0, "start": 0.0, "end": 5.0 }, { "widget": Slider, "name": "scale", "value": 1.0, "start": -5.0, "end": 5.0 }, ]
class Popup(Callback): __implementation__ = """ _ = require "underscore" Util = require "util/util" HasProperties = require "common/has_properties" class Popup extends HasProperties type: "Popup" execute: (data_source) -> for i in Util.get_indices(data_source) message = Util.replace_placeholders(@get("message"), data_source, i) window.alert(message) null defaults: -> return _.extend {}, super(), { message: "" } module.exports = Model: Popup """ message = String("", help=""" Message to display in a popup window. This can be a template string, which will be formatted with data from the data source. """)
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 StatsBox(BaseBox): __implementation__ = load_component('./stats_box.coffee') styles = String(default=None) display_items = Dict(String, Any, default=None) @validation.warning(EMPTY_LAYOUT) def _check_empty_layout(self): pass
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 LinearAxis(GuideRenderer): type = String("linear_axis") dimension = Int(0) location = Either(String('min'), Float) bounds = String('auto') axis_label = String axis_label_standoff = Int axis_label_props = Include(TextProps, prefix="axis_label") major_label_standoff = Int major_label_orientation = Either(Enum("horizontal", "vertical"), Int) major_label_props = Include(TextProps, prefix="major_label") # Line props axis_props = Include(LineProps, prefix="axis") tick_props = Include(LineProps, prefix="major_tick") major_tick_in = Int major_tick_out = Int
class Bin(Stat): """Represents a single bin of data values and attributes of the bin.""" label = String() start = Float() stop = Float() start_label = String() stop_label = String() center = Float() stat = Instance(Stat, default=Count()) def __init__(self, bin_label, values, **properties): properties['label'] = bin_label properties['start'], properties['stop'] = self.binstr_to_list( bin_label) properties['center'] = (properties['start'] + properties['stop']) / 2.0 properties['values'] = values super(Bin, self).__init__(**properties) @staticmethod def binstr_to_list(bins): """Produce a consistent display of a bin of data.""" value_chunks = bins.split(',') value_chunks = [ val.replace('[', '').replace(']', '').replace('(', '').replace(')', '') for val in value_chunks ] bin_values = [float(value) for value in value_chunks] return bin_values[0], bin_values[1] def update(self): self.stat.set_data(self.values) def calculate(self): self.value = self.stat.value
class PointGlyph(XyGlyph): """A set of glyphs placed in x,y coordinates with the same attributes.""" fill_color = String(default=DEFAULT_PALETTE[1]) fill_alpha = Float(default=0.7) marker = String(default='circle') size = Float(default=8) def __init__(self, x=None, y=None, line_color=None, fill_color=None, marker=None, size=None, **kwargs): kwargs['x'] = x kwargs['y'] = y kwargs['line_color'] = line_color or self.line_color kwargs['fill_color'] = fill_color or self.fill_color kwargs['marker'] = marker or self.marker kwargs['size'] = size or self.size super(PointGlyph, self).__init__(**kwargs) def get_glyph(self): return marker_types[self.marker] def build_renderers(self): glyph_type = self.get_glyph() glyph = glyph_type(x='x_values', y='y_values', line_color=self.line_color, fill_color=self.fill_color, size=self.size, fill_alpha=self.fill_alpha, line_alpha=self.line_alpha) yield GlyphRenderer(glyph=glyph)
class GuideRenderer(PlotObject): plot = Instance dimension = Int(0) location = String('min') bounds = String('auto') def __init__(self, **kwargs): super(GuideRenderer, self).__init__(**kwargs) if self.plot is not None: if self not in self.plot.renderers: self.plot.renderers.append(self) def vm_serialize(self): props = self.vm_props(withvalues=True) guide_props = {} for name in ("dimension", "location", "bounds"): if name in props: guide_props[name] = props.pop(name) del props["plot"] props.update({ "id": self._id, "plot": self.plot, "guidespec": guide_props }) return props @classmethod def load_json(cls, attrs, instance=None): """Loads all json into a instance of cls, EXCEPT any references which are handled in finalize """ inst = super(GuideRenderer, cls).load_json(attrs, instance=instance) if hasattr(inst, 'guidespec'): guidespec = inst.guidespec del inst.guidespec inst.update(**guidespec) return inst
class StockInputModel(VBoxModelForm): """Input Widgets, define the fields you want to read from the input here as bokeh properties input_specs is a list of dictionary, specifying how the kind of input widget you want for each property. the name field must match one of the properties, for example here, we use names of offset and scale. You can also specify title, if you want a different label in the generated form """ ticker1 = String(default="AAPL") ticker2 = String(default="GOOG") input_specs = [{ "widget": Select, "name": "ticker1", "value": "AAPL", "options": ["AAPL", "GOOG", "INTC", "BRCM", "YHOO"] }, { "widget": Select, "name": "ticker2", "value": "GOOG", "options": ["AAPL", "GOOG", "INTC", "BRCM", "YHOO"] }]
class App(PlotObject): data_source = Instance(ColumnDataSource) scatter_plot = Instance(Plot) stats = String() def update(self, **kwargs): super(App, self).update(**kwargs) if self.data_source: self.data_source.on_change('selected', self, 'selection_change') def selection_change(self, obj, attrname, old, new): pandas_df = pd.DataFrame(self.data_source.data) selected = self.data_source.selected if selected: pandas_df = pandas_df.iloc[selected, :] stats = pandas_df.describe() self.stats = str(stats)
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 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 Namespace(PlotObject): datasets = Dict() name = String() def __str__(self): return "Namespace(name=%r, datasets=%s)" % ( self.name, sorted(self.datasets.keys())) __repr__ = __str__ def _namespace(self): return get_ipython().user_ns def statsmodels(self): """Populate namespace with statsmodels' datasets. """ from statsmodels import datasets ns = self._namespace() for name, dataset in datasets.__dict__.iteritems(): if hasattr(dataset, "load_pandas"): ns[name] = dataset.load_pandas().data def populate(self, to_disk=True): """Scan global namespace for pandas' datasets. """ ns = self._namespace() datasets = {} for name, dataset in ns.iteritems(): if isinstance(dataset, DataFrame) and not name.startswith("_"): datasets[name] = list(dataset.columns) if datasets == self.datasets: return self.datasets = datasets self.session.store_obj(self) if not to_disk: return to_write = dict([(name, ns[name]) for name in datasets.keys()]) with open(self.filename, "w+") as file: pickle.dump(to_write, file, protocol=-1) def clean(self): """Remove all pandas' datasets from global namespace. """ ns = self._namespace() for name, dataset in dict(ns).iteritems(): if isinstance(dataset, DataFrame) and not name.startswith("_"): del ns[name] @property def filename(self): return self.name + ".pickle" def load(self): ns = self._namespace() if os.path.exists(self.filename): fname = self.filename with open(fname) as f: data = pickle.load(f) for k, v in data.iteritems(): ns[k] = v
class Foo(HasProps): x = Int(12) y = String("hello") z = Array(Int, np.array([1, 2, 3])) s = String(None)
class Foo(HasProps): x = Int(12) y = Enum("red", "blue", "green") z = String("blah")
def test_String(self): prop = String() 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.assertTrue(prop.is_valid("")) self.assertFalse(prop.is_valid(())) self.assertFalse(prop.is_valid([])) self.assertFalse(prop.is_valid({})) self.assertFalse(prop.is_valid(Foo()))
class Base(HasProps): x = Int(12) y = String("hello")
def test_String(self): prop = String() 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.assertTrue(prop.is_valid("")) self.assertFalse(prop.is_valid(())) self.assertFalse(prop.is_valid([])) self.assertFalse(prop.is_valid({})) self.assertFalse(prop.is_valid(Foo()))
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 StockApp(VBox): extra_generated_classes = [["StockApp", "StockApp", "VBox"]] jsmodel = "VBox" # text statistics pretext = Instance(PreText) # plots plot = Instance(Plot) line_plot1 = Instance(Plot) line_plot2 = Instance(Plot) hist1 = Instance(Plot) hist2 = Instance(Plot) # data source source = Instance(ColumnDataSource) # layout boxes mainrow = Instance(HBox) histrow = Instance(HBox) statsbox = Instance(VBox) # inputs ticker1 = String(default="AAPL") ticker2 = String(default="GOOG") ticker1_select = Instance(Select) ticker2_select = Instance(Select) input_box = Instance(VBoxForm) def __init__(self, *args, **kwargs): super(StockApp, self).__init__(*args, **kwargs) self._dfs = {} @classmethod def create(cls): """ This function is called once, and is responsible for creating all objects (plots, datasources, etc) """ # create layout widgets obj = cls() obj.mainrow = HBox() obj.histrow = HBox() obj.statsbox = VBox() obj.input_box = VBoxForm() # create input widgets obj.make_inputs() # outputs obj.pretext = PreText(text="", width=500) obj.make_source() obj.make_plots() obj.make_stats() # layout obj.set_children() return obj def make_inputs(self): self.ticker1_select = Select( name='ticker1', value='AAPL', options=['AAPL', 'GOOG', 'INTC', 'BRCM', 'YHOO']) self.ticker2_select = Select( name='ticker2', value='GOOG', options=['AAPL', 'GOOG', 'INTC', 'BRCM', 'YHOO']) @property def selected_df(self): pandas_df = self.df selected = self.source.selected if selected: pandas_df = pandas_df.iloc[selected, :] return pandas_df def make_source(self): self.source = ColumnDataSource(data=self.df) def line_plot(self, ticker, x_range=None): p = figure(title=ticker, x_range=x_range, x_axis_type='datetime', plot_width=1000, plot_height=200, title_text_font_size="10pt", tools="pan,wheel_zoom,box_select,reset") p.circle('date', ticker, size=2, source=self.source, nonselection_alpha=0.02) return p def hist_plot(self, ticker): global_hist, global_bins = np.histogram(self.df[ticker + "_returns"], bins=50) hist, bins = np.histogram(self.selected_df[ticker + "_returns"], bins=50) width = 0.7 * (bins[1] - bins[0]) center = (bins[:-1] + bins[1:]) / 2 start = global_bins.min() end = global_bins.max() top = hist.max() p = figure( title="%s hist" % ticker, plot_width=500, plot_height=200, tools="", title_text_font_size="10pt", x_range=[start, end], y_range=[0, top], ) p.rect(center, hist / 2.0, width, hist) return p def make_plots(self): ticker1 = self.ticker1 ticker2 = self.ticker2 p = figure( title="%s vs %s" % (ticker1, ticker2), plot_width=400, plot_height=400, tools="pan,wheel_zoom,box_select,reset", title_text_font_size="10pt", ) p.circle(ticker1 + "_returns", ticker2 + "_returns", size=2, nonselection_alpha=0.02, source=self.source) self.plot = p self.line_plot1 = self.line_plot(ticker1) self.line_plot2 = self.line_plot(ticker2, self.line_plot1.x_range) self.hist_plots() def hist_plots(self): ticker1 = self.ticker1 ticker2 = self.ticker2 self.hist1 = self.hist_plot(ticker1) self.hist2 = self.hist_plot(ticker2) def set_children(self): self.children = [ self.mainrow, self.histrow, self.line_plot1, self.line_plot2 ] self.mainrow.children = [self.input_box, self.plot, self.statsbox] self.input_box.children = [self.ticker1_select, self.ticker2_select] self.histrow.children = [self.hist1, self.hist2] self.statsbox.children = [self.pretext] def input_change(self, obj, attrname, old, new): if obj == self.ticker2_select: self.ticker2 = new if obj == self.ticker1_select: self.ticker1 = new self.make_source() self.make_plots() self.set_children() curdoc().add(self) def setup_events(self): super(StockApp, self).setup_events() if self.source: self.source.on_change('selected', self, 'selection_change') if self.ticker1_select: self.ticker1_select.on_change('value', self, 'input_change') if self.ticker2_select: self.ticker2_select.on_change('value', self, 'input_change') def make_stats(self): stats = self.selected_df.describe() self.pretext.text = str(stats) def selection_change(self, obj, attrname, old, new): self.make_stats() self.hist_plots() self.set_children() curdoc().add(self) @property def df(self): return get_data(self.ticker1, self.ticker2)
class ModelThatOverridesName(Model): name = String()
class DataSlider(PlotObject): plot = Instance(Plot, has_ref=True) data_source = Instance(has_ref=True) field = String()