#!/usr/bin/env python # coding: utf-8 # In[ ]: import pandas as pd import numpy as np symbol = "Security 1" symbol2 = "Security 2" # In[ ]: price_data = pd.DataFrame( np.cumsum(np.random.randn(150, 2).dot([[0.5, 0.4], [0.4, 1.0]]), axis=0) + 100, columns=["Security 1", "Security 2"], index=pd.date_range(start="01-01-2007", periods=150), ) dates_actual = price_data.index.values prices = price_data[symbol].values # In[ ]: from bqplot import DateScale, LinearScale, Axis, Lines, Scatter, Bars, Hist, Figure from bqplot.interacts import ( FastIntervalSelector, IndexSelector, BrushIntervalSelector, BrushSelector, MultiSelector, LassoSelector, PanZoom, HandDraw, ) from traitlets import link from ipywidgets import ToggleButtons, VBox, HTML # # Line Chart Selectors # ## Fast Interval Selector # In[ ]: ## First we define a Figure dt_x_fast = DateScale() lin_y = LinearScale() x_ax = Axis(label="Index", scale=dt_x_fast) x_ay = Axis(label=(symbol + " Price"), scale=lin_y, orientation="vertical") lc = Lines( x=dates_actual, y=prices, scales={"x": dt_x_fast, "y": lin_y}, colors=["orange"] ) lc_2 = Lines( x=dates_actual[50:], y=prices[50:] + 2, scales={"x": dt_x_fast, "y": lin_y}, colors=["blue"], ) # In[ ]: ## Next we define the type of selector we would like intsel_fast = FastIntervalSelector(scale=dt_x_fast, marks=[lc, lc_2]) # In[ ]: ## Now, we define a function that will be called when the FastIntervalSelector is interacted with def fast_interval_change_callback(change): db_fast.value = "The selected period is " + str(change.new) # In[ ]: ## Now we connect the selectors to that function intsel_fast.observe(fast_interval_change_callback, names=["selected"]) # In[ ]: ## We use the HTML widget to see the value of what we are selecting and modify it when an interaction is performed ## on the selector db_fast = HTML() db_fast.value = "The selected period is " + str(intsel_fast.selected) fig_fast_intsel = Figure( marks=[lc, lc_2], axes=[x_ax, x_ay], title="Fast Interval Selector Example", interaction=intsel_fast, ) # This is where we assign the interaction to this particular Figure VBox([db_fast, fig_fast_intsel]) # ## Index Selector # In[ ]: db_index = HTML(value="[]") # In[ ]: ## Now we try a selector made to select all the y-values associated with a single x-value index_sel = IndexSelector(scale=dt_x_fast, marks=[lc, lc_2]) # In[ ]: ## Now, we define a function that will be called when the selectors are interacted with def index_change_callback(change): db_index.value = "The selected date is " + str(change.new) # In[ ]: index_sel.observe(index_change_callback, names=["selected"]) # In[ ]: fig_index_sel = Figure( marks=[lc, lc_2], axes=[x_ax, x_ay], title="Index Selector Example", interaction=index_sel, ) VBox([db_index, fig_index_sel]) # ## Returning indexes of selected values # In[ ]: from datetime import datetime as py_dtime dt_x_index = DateScale(min=np.datetime64(py_dtime(2006, 6, 1))) lin_y2 = LinearScale() lc2_index = Lines(x=dates_actual, y=prices, scales={"x": dt_x_index, "y": lin_y2}) x_ax1 = Axis(label="Date", scale=dt_x_index) x_ay2 = Axis(label=(symbol + " Price"), scale=lin_y2, orientation="vertical") # In[ ]: intsel_date = FastIntervalSelector(scale=dt_x_index, marks=[lc2_index]) # In[ ]: db_date = HTML() db_date.value = str(intsel_date.selected) # In[ ]: ## Now, we define a function that will be called when the selectors are interacted with - a callback def date_interval_change_callback(change): db_date.value = str(change.new) # In[ ]: ## Notice here that we call the observe on the Mark lc2_index rather than on the selector intsel_date lc2_index.observe(date_interval_change_callback, names=["selected"]) fig_date_mark = Figure( marks=[lc2_index], axes=[x_ax1, x_ay2], title="Fast Interval Selector Selected Indices Example", interaction=intsel_date, ) VBox([db_date, fig_date_mark]) # ## Brush Selector # ### We can do the same with any type of selector # In[ ]: ## Defining a new Figure dt_x_brush = DateScale(min=np.datetime64(py_dtime(2006, 6, 1))) lin_y2_brush = LinearScale() lc3_brush = Lines(x=dates_actual, y=prices, scales={"x": dt_x_brush, "y": lin_y2_brush}) x_ax_brush = Axis(label="Date", scale=dt_x_brush) x_ay_brush = Axis(label=(symbol + " Price"), scale=lin_y2_brush, orientation="vertical") # In[ ]: db_brush = HTML(value="[]") # In[ ]: brushsel_date = BrushIntervalSelector( scale=dt_x_brush, marks=[lc3_brush], color="FireBrick" ) # In[ ]: ## Now, we define a function that will be called when the selectors are interacted with - a callback def date_brush_change_callback(change): db_brush.value = str(change.new) # In[ ]: lc3_brush.observe(date_brush_change_callback, names=["selected"]) # In[ ]: fig_brush_sel = Figure( marks=[lc3_brush], axes=[x_ax_brush, x_ay_brush], title="Brush Selector Selected Indices Example", interaction=brushsel_date, ) VBox([db_brush, fig_brush_sel]) # # Scatter Chart Selectors # ## Brush Selector # In[ ]: date_fmt = "%m-%d-%Y" sec2_data = price_data[symbol2].values dates = price_data.index.values # In[ ]: sc_x = LinearScale() sc_y = LinearScale() scatt = Scatter(x=prices, y=sec2_data, scales={"x": sc_x, "y": sc_y}) sc_xax = Axis(label=(symbol), scale=sc_x) sc_yax = Axis(label=(symbol2), scale=sc_y, orientation="vertical") # In[ ]: br_sel = BrushSelector(x_scale=sc_x, y_scale=sc_y, marks=[scatt], color="red") db_scat_brush = HTML(value="[]") # In[ ]: ## call back for the selector def brush_callback(change): db_scat_brush.value = str(br_sel.selected) # In[ ]: br_sel.observe(brush_callback, names=["brushing"]) # In[ ]: fig_scat_brush = Figure( marks=[scatt], axes=[sc_xax, sc_yax], title="Scatter Chart Brush Selector Example", interaction=br_sel, ) # In[ ]: VBox([db_scat_brush, fig_scat_brush]) # ## Brush Selector with Date Values # In[ ]: sc_brush_dt_x = DateScale(date_format=date_fmt) sc_brush_dt_y = LinearScale() scatt2 = Scatter( x=dates_actual, y=sec2_data, scales={"x": sc_brush_dt_x, "y": sc_brush_dt_y} ) # In[ ]: br_sel_dt = BrushSelector(x_scale=sc_brush_dt_x, y_scale=sc_brush_dt_y, marks=[scatt2]) # In[ ]: db_brush_dt = HTML(value=str(br_sel_dt.selected)) # In[ ]: ## call back for the selector def brush_dt_callback(change): db_brush_dt.value = str(br_sel_dt.selected) # In[ ]: br_sel_dt.observe(brush_dt_callback, names=["brushing"]) # In[ ]: sc_xax = Axis(label=(symbol), scale=sc_brush_dt_x) sc_yax = Axis(label=(symbol2), scale=sc_brush_dt_y, orientation="vertical") fig_brush_dt = Figure( marks=[scatt2], axes=[sc_xax, sc_yax], title="Brush Selector with Dates Example", interaction=br_sel_dt, ) # In[ ]: VBox([db_brush_dt, fig_brush_dt]) # # Histogram Selectors # In[ ]: ## call back for selectors def interval_change_callback(name, value): db3.value = str(value) ## call back for the selector def brush_callback(change): if not br_intsel.brushing: db3.value = str(br_intsel.selected) # In[ ]: returns = np.log(prices[1:]) - np.log(prices[:-1]) hist_x = LinearScale() hist_y = LinearScale() hist = Hist(sample=returns, scales={"sample": hist_x, "count": hist_y}) br_intsel = BrushIntervalSelector(scale=hist_x, marks=[hist]) br_intsel.observe(brush_callback, names=["selected"]) br_intsel.observe(brush_callback, names=["brushing"]) db3 = HTML() db3.value = str(br_intsel.selected) h_xax = Axis( scale=hist_x, label="Returns", grids="off", set_ticks=True, tick_format="0.2%" ) h_yax = Axis(scale=hist_y, label="Freq", orientation="vertical", grid_lines="none") fig_hist = Figure( marks=[hist], axes=[h_xax, h_yax], title="Histogram Selection Example", interaction=br_intsel, ) VBox([db3, fig_hist]) # ## Multi Selector # # * This selector provides the ability to have multiple brush selectors on the same graph. # * The first brush works like a regular brush. # * `Ctrl + click` creates a new brush, which works like the regular brush. # * The `active` brush has a Green border while all the `inactive` brushes have a Red border. # * `Shift + click` deactivates the current `active` brush. Now, click on any `inactive` brush to make it `active`. # * `Ctrl + Alt + Shift + click` clears and resets all the brushes. # In[ ]: def multi_sel_callback(change): if not multi_sel.brushing: db4.value = str(multi_sel.selected) # In[ ]: line_x = LinearScale() line_y = LinearScale() line = Lines( x=np.arange(100), y=np.random.randn(100), scales={"x": line_x, "y": line_y} ) multi_sel = MultiSelector(scale=line_x, marks=[line]) multi_sel.observe(multi_sel_callback, names=["selected"]) multi_sel.observe(multi_sel_callback, names=["brushing"]) db4 = HTML() db4.value = str(multi_sel.selected) h_xax = Axis(scale=line_x, label="Returns", grid_lines="none") h_yax = Axis(scale=hist_y, label="Freq", orientation="vertical", grid_lines="none") fig_multi = Figure( marks=[line], axes=[h_xax, h_yax], title="Multi-Selector Example", interaction=multi_sel, ) VBox([db4, fig_multi]) # In[ ]: # changing the names of the intervals. multi_sel.names = ["int1", "int2", "int3"] # ## Multi Selector with Date X # In[ ]: def multi_sel_dt_callback(change): if not multi_sel_dt.brushing: db_multi_dt.value = str(multi_sel_dt.selected) # In[ ]: line_dt_x = DateScale(min=np.datetime64(py_dtime(2007, 1, 1))) line_dt_y = LinearScale() line_dt = Lines( x=dates_actual, y=sec2_data, scales={"x": line_dt_x, "y": line_dt_y}, colors=["red"] ) multi_sel_dt = MultiSelector(scale=line_dt_x) multi_sel_dt.observe(multi_sel_dt_callback, names=["selected"]) multi_sel_dt.observe(multi_sel_dt_callback, names=["brushing"]) db_multi_dt = HTML() db_multi_dt.value = str(multi_sel_dt.selected) h_xax_dt = Axis(scale=line_dt_x, label="Returns", grid_lines="none") h_yax_dt = Axis( scale=line_dt_y, label="Freq", orientation="vertical", grid_lines="none" ) fig_multi_dt = Figure( marks=[line_dt], axes=[h_xax_dt, h_yax_dt], title="Multi-Selector with Date Example", interaction=multi_sel_dt, ) VBox([db_multi_dt, fig_multi_dt]) # ## Lasso Selector # In[ ]: lasso_sel = LassoSelector() # In[ ]: xs, ys = LinearScale(), LinearScale() data = np.arange(20) line_lasso = Lines(x=data, y=data, scales={"x": xs, "y": ys}) scatter_lasso = Scatter(x=data, y=data, scales={"x": xs, "y": ys}, colors=["skyblue"]) bar_lasso = Bars(x=data, y=data / 2.0, scales={"x": xs, "y": ys}) xax_lasso, yax_lasso = Axis(scale=xs, label="X"), Axis( scale=ys, label="Y", orientation="vertical" ) fig_lasso = Figure( marks=[scatter_lasso, line_lasso, bar_lasso], axes=[xax_lasso, yax_lasso], title="Lasso Selector Example", interaction=lasso_sel, ) lasso_sel.marks = [scatter_lasso, line_lasso] fig_lasso # In[ ]: scatter_lasso.selected, line_lasso.selected # ## Pan Zoom # In[ ]: xs_pz = DateScale(min=np.datetime64(py_dtime(2007, 1, 1))) ys_pz = LinearScale() line_pz = Lines( x=dates_actual, y=sec2_data, scales={"x": xs_pz, "y": ys_pz}, colors=["red"] ) panzoom = PanZoom(scales={"x": [xs_pz], "y": [ys_pz]}) xax = Axis(scale=xs_pz, label="Date", grids="off") yax = Axis(scale=ys_pz, label="Price", orientation="vertical", grid_lines="none") Figure(marks=[line_pz], axes=[xax, yax], interaction=panzoom) # ## Hand Draw # In[ ]: xs_hd = DateScale(min=np.datetime64(py_dtime(2007, 1, 1))) ys_hd = LinearScale() line_hd = Lines( x=dates_actual, y=sec2_data, scales={"x": xs_hd, "y": ys_hd}, colors=["red"] ) handdraw = HandDraw(lines=line_hd) xax = Axis(scale=xs_hd, label="Date", grid_lines="none") yax = Axis(scale=ys_hd, label="Price", orientation="vertical", grid_lines="none") Figure(marks=[line_hd], axes=[xax, yax], interaction=handdraw) # # Unified Figure with All Interactions # In[ ]: dt_x = DateScale(date_format=date_fmt, min=py_dtime(2007, 1, 1)) lc1_x = LinearScale() lc2_y = LinearScale() lc2 = Lines( x=np.linspace(0.0, 10.0, len(prices)), y=prices * 0.25, scales={"x": lc1_x, "y": lc2_y}, display_legend=True, labels=["Security 1"], ) lc3 = Lines( x=dates_actual, y=sec2_data, scales={"x": dt_x, "y": lc2_y}, colors=["red"], display_legend=True, labels=["Security 2"], ) lc4 = Lines( x=np.linspace(0.0, 10.0, len(prices)), y=sec2_data * 0.75, scales={"x": LinearScale(min=5, max=10), "y": lc2_y}, colors=["green"], display_legend=True, labels=["Security 2 squared"], ) x_ax1 = Axis(label="Date", scale=dt_x) x_ax2 = Axis(label="Time", scale=lc1_x, side="top", grid_lines="none") x_ay2 = Axis(label=(symbol + " Price"), scale=lc2_y, orientation="vertical") fig = Figure(marks=[lc2, lc3, lc4], axes=[x_ax1, x_ax2, x_ay2]) # In[ ]: ## declaring the interactions multi_sel = MultiSelector(scale=dt_x, marks=[lc2, lc3]) br_intsel = BrushIntervalSelector(scale=lc1_x, marks=[lc2, lc3]) index_sel = IndexSelector(scale=dt_x, marks=[lc2, lc3]) int_sel = FastIntervalSelector(scale=dt_x, marks=[lc3, lc2]) hd = HandDraw(lines=lc2) hd2 = HandDraw(lines=lc3) pz = PanZoom(scales={"x": [dt_x], "y": [lc2_y]}) deb = HTML() deb.value = "[]" # In[ ]: ## Call back handler for the interactions def test_callback(change): deb.value = str(change.new) multi_sel.observe(test_callback, names=["selected"]) br_intsel.observe(test_callback, names=["selected"]) index_sel.observe(test_callback, names=["selected"]) int_sel.observe(test_callback, names=["selected"]) # In[ ]: from collections import OrderedDict selection_interacts = ToggleButtons( options=OrderedDict( [ ("HandDraw1", hd), ("HandDraw2", hd2), ("PanZoom", pz), ("FastIntervalSelector", int_sel), ("IndexSelector", index_sel), ("BrushIntervalSelector", br_intsel), ("MultiSelector", multi_sel), ("None", None), ] ) ) link((selection_interacts, "value"), (fig, "interaction")) VBox([deb, fig, selection_interacts], align_self="stretch") # In[ ]: # Set the scales of lc4 to the ones of lc2 and check if panzoom pans the two. lc4.scales = lc2.scales