def test_buffer_stream(self): stream = Buffer(data=self.df, index=False) plot = self.df.hvplot('x', 'y', stream=stream) pd.testing.assert_frame_equal(plot[()].data, self.df) new_df = pd.DataFrame([[7, 8], [9, 10]], columns=['x', 'y']) stream.send(new_df) pd.testing.assert_frame_equal(plot[()].data, pd.concat([self.df, new_df]))
def test_buffer_stream_following(self): stream = Buffer(data={ 'x': np.array([1]), 'y': np.array([1]) }, following=True) dmap = DynamicMap(Curve, streams=[stream]) plot = bokeh_renderer.get_plot(dmap) x_range = plot.handles['x_range'] y_range = plot.handles['y_range'] self.assertEqual(x_range.start, 0) self.assertEqual(x_range.end, 2) self.assertEqual(y_range.start, 0) self.assertEqual(y_range.end, 2) stream.send({'x': np.array([2]), 'y': np.array([-1])}) self.assertEqual(x_range.start, 1) self.assertEqual(x_range.end, 2) self.assertEqual(y_range.start, -1) self.assertEqual(y_range.end, 1) stream.following = False stream.send({'x': np.array([3]), 'y': np.array([3])}) self.assertEqual(x_range.start, 1) self.assertEqual(x_range.end, 2) self.assertEqual(y_range.start, -1) self.assertEqual(y_range.end, 1)
def test_spread_stream_data(self): buffer = Buffer({'y': np.array([]), 'yerror': np.array([]), 'x': np.array([])}) dmap = DynamicMap(Spread, streams=[buffer]) plot = bokeh_renderer.get_plot(dmap) buffer.send({'y': [1, 2, 1, 4], 'yerror': [.5, .2, .1, .5], 'x': [0,1,2,3]}) cds = plot.handles['cds'] self.assertEqual(cds.data['x'], np.array([0., 1., 2., 3., 3., 2., 1., 0.])) self.assertEqual(cds.data['y'], np.array([0.5, 1.8, 0.9, 3.5, 4.5, 1.1, 2.2, 1.5]))
def test_dynamic_groupby_kdims_and_streams(self): def plot_function(mydim, data): return Scatter(data[data[:, 2]==mydim]) buff = Buffer(data=np.empty((0, 3))) dmap = DynamicMap(plot_function, streams=[buff], kdims='mydim').redim.values(mydim=[0, 1, 2]) ndlayout = dmap.groupby('mydim', container_type=NdLayout) self.assertIsInstance(ndlayout[0], DynamicMap) data = np.array([(0, 0, 0), (1, 1, 1), (2, 2, 2)]) buff.send(data) self.assertEqual(ndlayout[0][()], Scatter([(0, 0)])) self.assertEqual(ndlayout[1][()], Scatter([(1, 1)])) self.assertEqual(ndlayout[2][()], Scatter([(2, 2)]))
class BokehApp: def __init__(self): self.switch_key = 'peak_8' self.maxlen = 1000000 # Initialize buffers self.b_th_peak = Buffer(pd.DataFrame({ 'peak': [], 'lowerbound': [], 'higherbound': [] }), length=1000000) # Initialize callbacks self.callback_id_th_b = None def clear_buffer(self): """ Modified version of hv.buffer.clear() since original appears to be buggy Clear buffer/graph whenever switch is toggled """ with util.disable_constant(self.b_th_peak): self.b_th_peak.data = self.b_th_peak.data.iloc[:0] self.b_th_peak.send( pd.DataFrame({ 'peak': [], 'lowerbound': [], 'higherbound': [] })) def produce_timehistory(self, context, doc): """ Create timetool data timehistory Parameters ---------- context = zmq.Context() Creates zmq socket to receive data doc: bokeh.document (I think) Bokeh document to be displayed on webpage """ # Port to connect to master port = 5000 socket = context.socket(zmq.REQ) # MUST BE FROM SAME MACHINE, CHANGE IF NECESSARY!!! socket.connect("tcp://localhost:%d" % port) # Dynamic Maps plot_peak_b = hv.DynamicMap(partial(hv.Curve, kdims=['index', 'peak']), streams=[self.b_th_peak]).options( width=1000, finalize_hooks=[ apply_formatter ]).redim.label(index='Time in UTC') plot_peak_std_low = hv.DynamicMap( partial(hv.Curve, kdims=['index', 'lowerbound']), streams=[self.b_th_peak]).options( line_alpha=0.5, width=1000, line_color='gray', finalize_hooks=[apply_formatter ]).redim.label(index='Time in UTC') plot_peak_std_high = hv.DynamicMap( partial(hv.Curve, kdims=['index', 'higherbound']), streams=[self.b_th_peak]).options( line_alpha=0.5, width=1000, line_color='gray').redim.label(index='Time in UTC') # Decimate and datashade pointTest = decimate(plot_peak_b, streams=[hv.streams.PlotSize]) test1 = datashade(plot_peak_std_low, streams=[hv.streams.PlotSize], normalization='linear').options( width=1000, finalize_hooks=[apply_formatter]) test2 = datashade(plot_peak_std_high, streams=[hv.streams.PlotSize], normalization='linear') plot = (test1 * test2 * pointTest) # Use bokeh to render plot hvplot = renderer.get_plot(plot, doc) def push_data(stream): """ Push data to timetool time history graph """ median = pd.DataFrame({'peak': []}) lowerbound = pd.DataFrame({'peak': []}) higherbound = pd.DataFrame({'peak': []}) # Request socket.send_string("Hello") print("Oof") data_dict = socket.recv_pyobj() peakDict = data_dict['peakDict'] peakTSDict = data_dict['peakTSDict'] TS_key = self.switch_key + '_TS' data = list(peakDict[self.switch_key]) timestamp = list(peakTSDict[TS_key]) times = [1000 * time for time in timestamp] dataSeries = pd.Series(data, index=times) zipped = basic_event_builder(peak=dataSeries) median = zipped.rolling(120, min_periods=1).median() std = zipped.rolling(120, min_periods=1).std() lowerbound = median - std higherbound = median + std df = pd.DataFrame({ 'peak': median['peak'], 'lowerbound': lowerbound['peak'], 'higherbound': higherbound['peak'] }) stream.send(df) def switch(attr, old, new): """ Update drop down menu value """ self.switch_key = select.value self.clear_buffer() print("Yes!") def play_graph(): """ Provide play and pause functionality to the graph """ if startButton.label == '► Play': startButton.label = '❚❚ Pause' self.callback_id_th_b = doc.add_periodic_callback( partial(push_data, stream=self.b_th_peak), 1000) else: startButton.label = '► Play' doc.remove_periodic_callback(self.callback_id_th_b) peak_list = [ 'peak_8', 'peak_9', 'peak_10', 'peak_11', 'peak_12', 'peak_13', 'peak_14', 'peak_15' ] select = Select(title='Peak:', value='peak_8', options=peak_list) select.on_change('value', switch) startButton = Button(label='❚❚ Pause') startButton.on_click(play_graph) self.callback_id_th_b = doc.add_periodic_callback( partial(push_data, stream=self.b_th_peak), 1000) plot = column(select, startButton, hvplot.state) doc.title = "Time History Graphs" doc.add_root(plot)
class HoloViewsConverter(object): def __init__(self, data, kind=None, by=None, width=700, height=300, shared_axes=False, columns=None, grid=False, legend=True, rot=None, title=None, xlim=None, ylim=None, xticks=None, yticks=None, fontsize=None, colormap=None, stacked=False, logx=False, logy=False, loglog=False, hover=True, style_opts={}, plot_opts={}, use_index=False, value_label='value', group_label='Group', colorbar=False, streaming=False, backlog=1000, timeout=1000, persist=False, use_dask=False, **kwds): # Validate DataSource if not isinstance(data, DataSource): raise TypeError('Can only plot intake DataSource types') elif data.container != 'dataframe': raise NotImplementedError('Plotting interface currently only ' 'supports DataSource objects with ' 'dataframe container.') self.data_source = data self.streaming = streaming self.use_dask = use_dask if streaming: self.data = data.read() self.stream = Buffer(self.data, length=backlog) if gen is None: raise ImportError('Streaming support requires tornado.') @gen.coroutine def f(): self.stream.send(data.read()) self.cb = PeriodicCallback(f, timeout) elif use_dask and dd is not None: ddf = data.to_dask() self.data = ddf.persist() if persist else ddf else: self.data = data.read() # High-level options self.by = by or [] self.columns = columns self.stacked = stacked self.use_index = use_index self.kwds = kwds self.value_label = value_label self.group_label = group_label # Process style options if 'cmap' in kwds and colormap: raise TypeError("Only specify one of `cmap` and `colormap`.") elif 'cmap' in kwds: cmap = kwds.pop('cmap') else: cmap = colormap self._style_opts = dict(**style_opts) if cmap: self._style_opts['cmap'] = cmap if 'size' in kwds: self._style_opts['size'] = kwds.pop('size') if 'alpha' in kwds: self._style_opts['alpha'] = kwds.pop('alpha') # Process plot options plot_options = dict(plot_opts) plot_options['logx'] = logx or loglog plot_options['logy'] = logy or loglog plot_options['show_grid'] = grid plot_options['shared_axes'] = shared_axes plot_options['show_legend'] = legend if xticks: plot_options['xticks'] = xticks if yticks: plot_options['yticks'] = yticks if width: plot_options['width'] = width if height: plot_options['height'] = height if fontsize: plot_options['fontsize'] = fontsize if colorbar: plot_options['colorbar'] = colorbar if self.kwds.get('vert', False): plot_options['invert_axes'] = True if rot: if (kind == 'barh' or kwds.get('orientation') == 'horizontal' or kwds.get('vert')): axis = 'yrotation' else: axis = 'xrotation' plot_options[axis] = rot if hover: plot_options['tools'] = ['hover'] self._hover = hover self._plot_opts = plot_options self._relabel = {'label': title} self._dim_ranges = { 'x': xlim or (None, None), 'y': ylim or (None, None) } self._norm_opts = {'framewise': True} @streaming def table(self, x=None, y=None, data=None): allowed = ['width', 'height'] opts = {k: v for k, v in self._plot_opts.items() if k in allowed} data = self.data if data is None else data return Table(data, self.columns, []).opts(plot=opts) def __call__(self, kind, x, y): return getattr(self, kind)(x, y) def single_chart(self, element, x, y, data=None): opts = { element.__name__: dict(plot=self._plot_opts, norm=self._norm_opts, style=self._style_opts) } ranges = {y: self._dim_ranges['y']} if x: ranges[x] = self._dim_ranges['x'] data = self.data if data is None else data ys = [y] if 'c' in self.kwds and self.kwds['c'] in data.columns: ys += [self.kwds['c']] if self.by: chart = Dataset(data, [self.by, x], ys).to(element, x, ys, self.by).overlay() else: chart = element(data, x, ys) return chart.redim.range(**ranges).relabel(**self._relabel).opts(opts) def chart(self, element, x, y, data=None): "Helper method for simple x vs. y charts" if x and y: return self.single_chart(element, x, y, data) # Note: Loading dask dataframe into memory due to rename bug data = (self.data if data is None else data) if self.use_dask: data = data.compute() opts = dict(plot=dict(self._plot_opts, labelled=['x']), norm=self._norm_opts, style=self._style_opts) if self.use_index or x: if self.use_index is not None and isinstance(self.use_index, bool): x = x or data.index.name or 'index' else: x = self.use_index columns = self.columns or data.columns charts = {} for c in columns: chart = element(data, x, c).redim(**{c: self.value_label}) ranges = {x: self._dim_ranges['x'], c: self._dim_ranges['y']} charts[c] = (chart.relabel(**self._relabel).redim.range( **ranges).opts(**opts)) return NdOverlay(charts) else: raise ValueError('Could not determine what to plot. Expected ' 'either x and y parameters to be declared ' 'or use_index to be enabled.') @streaming def line(self, x, y, data=None): return self.chart(Curve, x, y, data) @streaming def scatter(self, x, y, data=None): scatter = self.chart(Scatter, x, y, data) if 'c' in self.kwds: color_opts = { 'Scatter': { 'colorbar': self.kwds.get('colorbar', False), 'color_index': self.kwds['c'] } } return scatter.opts(plot=color_opts) return scatter @streaming def area(self, x, y, data=None): areas = self.chart(Area, x, y, data) if self.stacked: areas = areas.map(Area.stack, NdOverlay) return areas def _category_plot(self, element, data=None): """ Helper method to generate element from indexed dataframe. """ data = self.data if data is None else data if isinstance(self.use_index, bool): index = data.index.name or 'index' else: index = self.use_index kdims = [index, self.group_label] id_vars = [index] invert = not self.kwds.get('vert', True) opts = { 'plot': dict(self._plot_opts, labelled=[]), 'norm': self._norm_opts } ranges = {self.value_label: self._dim_ranges['y']} if self.columns: data = data[self.columns + id_vars] if dd and isinstance(data, dd.DataFrame): data = data.compute() df = pd.melt(data, id_vars=id_vars, var_name=self.group_label, value_name=self.value_label) return (element(df, kdims, self.value_label).redim.range( **ranges).relabel(**self._relabel).opts(**opts)) def _stats_plot(self, element, y, data=None): """ Helper method to generate element from indexed dataframe. """ data = self.data if data is None else data opts = { 'plot': dict(self._plot_opts, labelled=[]), 'norm': self._norm_opts, 'style': self._style_opts } if y: ranges = {y: self._dim_ranges['y']} kdims = [self.by] if self.by else [] return (element( data, kdims, y).redim.range(**ranges).relabel(**self._relabel).opts(**opts)) kdims = [self.group_label] ranges = {self.value_label: self._dim_ranges['y']} if self.columns: data = data[self.columns] if dd and isinstance(data, dd.DataFrame): data = data.compute() df = pd.melt(data, var_name=self.group_label, value_name=self.value_label) return (element(df, kdims, self.value_label).redim.range( **ranges).relabel(**self._relabel).opts(**opts)) @streaming def bar(self, x, y, data=None): if x and y: return self.single_chart(Bars, x, y, data) elif self.use_index: stack_index = 1 if self.stacked else None opts = {'Bars': {'stack_index': stack_index}} return self._category_plot(Bars, data).opts(plot=opts) else: raise ValueError('Could not determine what to plot. Expected ' 'either x and y parameters to be declared ' 'or use_index to be enabled.') @streaming def barh(self, x, y, data=None): return self.bar(x, y, data).opts(plot={'Bars': dict(invert_axes=True)}) @streaming def box(self, x, y, data=None): return self._stats_plot(BoxWhisker, y, data) @streaming def violin(self, x, y, data=None): try: from holoviews.element import Violin except ImportError: raise ImportError('Violin plot requires HoloViews version >=1.10') return self._stats_plot(Violin, y, data) @streaming def hist(self, x, y, data=None): plot_opts = dict(self._plot_opts) invert = self.kwds.get('orientation', False) == 'horizontal' opts = dict(plot=dict(plot_opts, labelled=['x'], invert_axes=invert), style=self._style_opts, norm=self._norm_opts) hist_opts = { 'num_bins': self.kwds.get('bins', 10), 'bin_range': self.kwds.get('bin_range', None), 'normed': self.kwds.get('normed', False) } data = self.data if data is None else data ds = Dataset(data) if y and self.by: return histogram(ds.to(Dataset, [], y, self.by), **hist_opts).\ overlay().opts({'Histogram': opts}) elif y: return histogram(ds, dimension=y, **hist_opts).\ opts({'Histogram': opts}) hists = {} columns = self.columns or data.columns for col in columns: hist = histogram(ds, dimension=col, **hist_opts) ranges = {hist.vdims[0].name: self._dim_ranges['y']} hists[col] = (hist.redim.range(**ranges).relabel( **self._relabel).opts(**opts)) return NdOverlay(hists) @streaming def kde(self, x, y, data=None): data = self.data if data is None else data plot_opts = dict(self._plot_opts) invert = self.kwds.get('orientation', False) == 'horizontal' opts = dict(plot=dict(plot_opts, invert_axes=invert), style=self._style_opts, norm=self._norm_opts) opts = { 'Distribution': opts, 'Area': opts, 'NdOverlay': { 'plot': dict(plot_opts, legend_limit=0) } } if y and self.by: ds = Dataset(data) return ds.to(Distribution, y, [], self.by).overlay().opts(opts) elif y: return Distribution(data, y, []).opts(opts) if self.columns: data = data[self.columns] df = pd.melt(data, var_name=self.group_label, value_name=self.value_label) ds = Dataset(df) if len(df): overlay = ds.to(Distribution, self.value_label).overlay() else: vdim = self.value_label + ' Density' overlay = NdOverlay({0: Area([], self.value_label, vdim)}, [self.group_label]) return overlay.relabel(**self._relabel).opts(opts) @streaming def heatmap(self, x, y, data=None): data = data or self.data if not x: x = data.columns[0] if not y: y = data.columns[1] z = self.kwds.get('C', data.columns[2]) opts = dict(plot=self._plot_opts, norm=self._norm_opts, style=self._style_opts) hmap = HeatMap(data, [x, y], z).opts(**opts) if 'reduce_function' in self.kwds: return hmap.aggregate(function=self.kwds['reduce_function']) return hmap
class Resident: def __init__(self, name:str, rh:ResidentHealth, move_freq:datetime.timedelta, move_dist_range:Tuple[float, float], ec:EpidemicConfig, cc:CityConfig ): """ :param move_freq: how frequently does the resident go out of their house :param move_dist_range: when they go out, what distance range do they travel """ self.name = name self.infected, self.infected_since = HealthStateEnum.NEVER_INFECTED, SENTINAL_DATE self.recovered_on, self.died_on = SENTINAL_DATE, SENTINAL_DATE self.health, self.ec, self.cc = rh, ec, cc self.move_freq, self.move_dist_range= move_freq, move_dist_range self.loc_hist:List[Tuple[datetime.datetime, float, float]] = [] self._loc_hist_replay_stream = None def _valid_coords(self, x:float, y:float): """Check if resident is at a valid location""" min_x, min_y, max_x, max_y = 0, 0, self.cc.length, self.cc.breadth return min_x <= x <= max_x and min_y <= y <= max_y def location(self): return self._x_pos, self._y_pos def as_df(self): return pd.DataFrame([self.__dict__]).drop(columns=["ec", "cc", "loc_hist"]).astype("str") def move_to(self, new_x:float, new_y:float, date:datetime.datetime): if self._valid_coords(new_x, new_y): self._x_pos, self._y_pos = new_x, new_y self.loc_hist.append((date, new_x, new_y)) return True return False def __repr__(self): return f"{self.name} is at ({self._x_pos}, {self._y_pos})" def __call__(self): infected_since = self.infected_since.strftime("%c") if self.infected_since < SENTINAL_DATE else None return self._x_pos, self._y_pos, self.name, self.infected, infected_since def is_infected(self): return self.infected == HealthStateEnum.INFECTED def is_alive(self): return self.infected != HealthStateEnum.DEAD def infect(self, date:datetime.datetime): if not self.is_alive(): return self.infected = HealthStateEnum.INFECTED self.infected_since = date def _did_recover(self): return self.health.recovery_prob > random.random() # TODO: Any better way to build this? def cure(self, date:datetime.datetime, epidemic_config:EpidemicConfig): """Returns true if the resident was successfully cured""" if not self.is_alive(): return duration_since_infected = date - self.infected_since if self.is_infected() and duration_since_infected >= epidemic_config.recover_after: if self._did_recover(): self.infected = HealthStateEnum.RECOVERED self.recovered_on = date return True if duration_since_infected > epidemic_config.decease_after: self.kill(date) return False def kill(self, date:datetime.datetime): self.infected = HealthStateEnum.DEAD self.died_on = date def progress_time(self, current_date:datetime.datetime, move_prob:float=1) -> bool: """ Make necessary changes to the state of the resident :param move_prob: for finer control over move_freq. Say resident goes / moves out once a week and we progress the simulator day by day, move_prob can be used to select the day of move during the week :return True if state of the resident changed or False otherwise """ if not self.is_alive(): return False last_moved_on, _, _ = self.loc_hist[-1] if last_moved_on + self.move_freq <= current_date and random.random() <= move_prob and self._move_to_rand(current_date): return True # as the resident moved this time return False def _move_to_rand(self, date:datetime.datetime, max_attempts:int=3) -> bool: """ :param max_attempts: max number of times to try to find a valid new position before giving up :returns True if the resident was able to move to a valid position """ for attempt_num in range(max_attempts): distance, direction = random.uniform(*self.move_dist_range), random.uniform(0, 2 * math.pi) new_x, new_y = self._x_pos + distance * math.cos(direction), self._y_pos + distance * math.sin(direction) move_ok = self.move_to(new_x, new_y, date) if move_ok: return True return False def initialize_loc_history_replay(self): self._loc_hist_replay_stream = Buffer(pd.DataFrame({ "x": pd.Series([], dtype=float), "y": pd.Series([], dtype=float), "Name": pd.Series([], dtype=str), "Date": pd.Series([], dtype=str) }), length=100, index=False) loc_dmap = hv.DynamicMap(partial(hv.Points, vdims=["Name", "Date"]), streams=[self._loc_hist_replay_stream]) trace_dmap = hv.DynamicMap(partial(hv.Curve), streams=[self._loc_hist_replay_stream]) # title_dmap = hv.DynamicMap(partial(hv.Text, x=13, y=13, text="Hello", vdims=["Date", "Name", "timestamp"], streams=[self._loc_hist_replay_stream])) print("Now call start_loc_history_replay() whenever you are ready") return (loc_dmap * trace_dmap). opts(ylim=(0, self.cc.breadth + 2), xlim=(0, self.cc.length + 2), show_legend=False).\ opts(opts.Points(size=6, tools=["hover"], color="Date", cmap="Blues")) def start_loc_history_replay(self, complete_within_sec:float=10): """ :param complete_within_sec: adjusts the playback speed accordingly """ self._loc_hist_replay_stream.clear() playback_speed = complete_within_sec / len(self.loc_hist) for date, x, y in self.loc_hist: sleep(playback_speed) self._loc_hist_replay_stream.send(pd.DataFrame([ (x, y, self.name, date.strftime("%c")) ], columns=["x", "y", "Name", "Date"])) def visualize_loc_history(self): df = pd.DataFrame(self.loc_hist, columns=["date", "x", "y"]) df["date"] = df["date"].apply(lambda d: d.strftime("%c")) scatter = hv.Scatter(df, kdims=["x", "y"], vdims=["date", "date"]).\ opts(tools=["hover"], color="date", cmap="Blues", size=6, padding=0.05, legend_position="bottom") line = hv.Curve(scatter, label="path") table = hv.Table(self.as_df().T.reset_index().rename(columns={"index": "Field", 0: "Details"})) return table + (line * scatter).opts(width=500, height=500, xlim=(0, self.cc.length), ylim=(0, self.cc.breadth))
class BokehApp: def __init__(self): self.switchButton = 'ipm2__sum' self.maxlen = 1000000 # Initialize buffers self.b_timetool = Buffer(pd.DataFrame({ 'timestamp': [], 'timetool': [] }), length=40000) self.b_IpmAmp = Buffer(pd.DataFrame({ 'timetool': [], 'ipm': [] }), length=1000) self.b_corr_timehistory = Buffer(pd.DataFrame({ 'timestamp': [], 'correlation': [] }), length=40000) # Initialize callbacks self.cb_id_timetool = None self.cb_id_amp_ipm = None self.cb_id_corr_timehistory = None def clear_buffer(self): """ Modified version of hv.buffer.clear() since original appears to be buggy Clear buffer/graph whenever switch is toggled """ data = pd.DataFrame({'timestamp': [], 'correlation': []}) with util.disable_constant( self.b_corr_timehistory) and util.disable_constant( self.b_IpmAmp): self.b_IpmAmp.data = self.b_IpmAmp.data.iloc[:0] self.b_corr_timehistory.data = self.b_corr_timehistory.data.iloc[: 0] self.b_IpmAmp.send(pd.DataFrame({'timetool': [], 'ipm': []})) self.b_corr_timehistory.send(data) def produce_graphs(self, context, doc): """ Create timetool data timehistory, timetool vs ipm, and correlation timehistory graphs. Parameters ---------- context = zmq.Context() Creates zmq socket to receive data doc: bokeh.document (I think) Bokeh document to be displayed on webpage """ port = 5006 socket = context.socket(zmq.SUB) # MUST BE FROM SAME MACHINE, CHANGE IF NECESSARY!!! socket.connect("tcp://psanagpu114:%d" % port) socket.setsockopt(zmq.SUBSCRIBE, b"") # Note: Cannot name 'timetool' variables in hvTimeTool and hvIpmAmp the same thing # Otherwise, holoviews will try to sync the axis and throw off the ranges for the plots # since hvIpmAmp only deals with the last 1000 points whereas hvTimeTool deals with all # the points hvTimeTool = hv.DynamicMap(my_partial(hv.Points, kdims=['timestamp', 'timetool']), streams=[self.b_timetool]).options( width=1000, finalize_hooks=[apply_formatter], xrotation=45).redim.label( timestamp='Time in UTC', timetool='Timetool Data') hvIpmAmp = hv.DynamicMap( my_partial(hv.Scatter, kdims=['timetool', 'ipm']), streams=[self.b_IpmAmp]).options(width=500).redim.label( timetool='Last 1000 Timetool Data Points', ipm='Last 1000 Ipm Data Points') hvCorrTimeHistory = hv.DynamicMap( my_partial(hv.Scatter, kdims=['timestamp', 'correlation']), streams=[self.b_corr_timehistory ]).options(width=500, finalize_hooks=[apply_formatter], xrotation=45).redim.label(time='Time in UTC') layout = (hvIpmAmp + hvCorrTimeHistory + hvTimeTool).cols(2) hvplot = renderer.get_plot(layout) def push_data_timetool(buffer): """ Push data to timetool time history graph """ timetool_d = deque(maxlen=self.maxlen) timetool_t = deque(maxlen=self.maxlen) if socket.poll(timeout=0): data_dict = socket.recv_pyobj() timetool_d = data_dict['tt__FLTPOS_PS'] # Get time from data_dict timeData = deque(maxlen=self.maxlen) for time in data_dict['event_time']: num1 = str(time[0]) num2 = str(time[1]) fullnum = num1 + "." + num2 timeData.append(float(fullnum)) timetool_t = timeData # Convert time to seconds so bokeh formatter can get correct datetime times = [1000 * time for time in list(timetool_t)] data = pd.DataFrame({'timestamp': times, 'timetool': timetool_d}) buffer.send(data) def push_data_amp_ipm(buffer): """ Push data into timetool amp vs ipm graph """ timetool_d = deque(maxlen=self.maxlen) ipm_d = deque(maxlen=self.maxlen) if socket.poll(timeout=0): data_dict = socket.recv_pyobj() timetool_d = data_dict['tt__AMPL'] ipm_d = data_dict[self.switchButton] data = pd.DataFrame({'timetool': timetool_d, 'ipm': ipm_d}) buffer.send(data) def push_data_corr_time_history(buffer): """ Calculate correlation between timetool amp and ipm and push to correlation time history graph """ timetool_d = deque(maxlen=self.maxlen) timetool_t = deque(maxlen=self.maxlen) ipm_d = deque(maxlen=self.maxlen) if socket.poll(timeout=0): data_dict = socket.recv_pyobj() timetool_d = data_dict['tt__FLTPOS_PS'] ipm_d = data_dict[self.switchButton] # Get time from data_dict timeData = deque(maxlen=self.maxlen) for time in data_dict['event_time']: num1 = str(time[0]) num2 = str(time[1]) fullnum = num1 + "." + num2 timeData.append(float(fullnum)) timetool_t = timeData # Convert time to seconds so bokeh formatter can get correct datetime times = [1000 * time for time in list(timetool_t)] data = pd.DataFrame({'timetool': timetool_d, 'ipm': ipm_d}) data_corr = data['timetool'].rolling(window=120).corr( other=data['ipm']) # Start at index 119 so we don't get null data final_df = pd.DataFrame({ 'timestamp': times[119:], 'correlation': data_corr[119:] }) buffer.send(final_df) def switch(attr, old, new): """ Update drop down menu value """ self.switchButton = select.value self.clear_buffer() def stop(): """ Add pause and play functionality to graph """ if stopButton.label == 'Play': stopButton.label = 'Pause' self.cb_id_timetool = doc.add_periodic_callback( partial(push_data_timetool, buffer=self.b_timetool), 1000) self.cb_id_amp_ipm = doc.add_periodic_callback( partial(push_data_amp_ipm, buffer=self.b_IpmAmp), 1000) self.cb_id_corr_timehistory = doc.add_periodic_callback( partial(push_data_corr_time_history, buffer=self.b_corr_timehistory), 1000) else: stopButton.label = 'Play' doc.remove_periodic_callback(self.cb_id_timetool) doc.remove_periodic_callback(self.cb_id_amp_ipm) doc.remove_periodic_callback(self.cb_id_corr_timehistory) # Start the callback self.cb_id_timetool = doc.add_periodic_callback( partial(push_data_timetool, buffer=self.b_timetool), 1000) self.cb_id_amp_ipm = doc.add_periodic_callback( partial(push_data_amp_ipm, buffer=self.b_IpmAmp), 1000) self.cb_id_corr_timehistory = doc.add_periodic_callback( partial(push_data_corr_time_history, buffer=self.b_corr_timehistory), 1000) # Use this to test since ipm2 and ipm3 are too similar to see any differences # select = Select(title='ipm value:', value='ipm2__sum', options=['ipm2__sum', 'tt__FLTPOS_PS']) select = Select(title='ipm value:', value='ipm2__sum', options=['ipm2__sum', 'ipm3__sum']) select.on_change('value', switch) stopButton = Button(label='Pause') stopButton.on_click(stop) plot = column(select, stopButton, hvplot.state) doc.add_root(plot)