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., 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