In [ ]:
from __future__ import print_function
import numpy as np
import pandas as pd
from IPython.display import display
from bqplot import (Axis, ColorAxis, LinearScale, DateScale, DateColorScale, OrdinalScale, OrdinalColorScale,
                    ColorScale, Scatter, Lines, Figure, Tooltip)
from ipywidgets import Label

Get Data


In [ ]:
price_data = pd.DataFrame(np.cumsum(np.random.randn(150, 2).dot([[1.0, -0.8], [-0.8, 1.0]]), axis=0) + 100,
                          columns=['Security 1', 'Security 2'], index=pd.date_range(start='01-01-2007', periods=150))
size = 100
np.random.seed(0)
x_data = range(size)
y_data = np.cumsum(np.random.randn(size) * 100.0)
ord_keys = np.array(['A', 'B', 'C', 'D', 'E', 'F'])
ordinal_data = np.random.randint(5, size=size)

In [ ]:
symbols = ['Security 1', 'Security 2']

dates_all = price_data.index.values
dates_all_t = dates_all[1:]
sec1_levels = np.array(price_data[symbols[0]].values.flatten())
log_sec1 = np.log(sec1_levels)
sec1_returns = log_sec1[1:] - log_sec1[:-1]

sec2_levels = np.array(price_data[symbols[1]].values.flatten())

Basic Scatter


In [ ]:
sc_x = DateScale()
sc_y = LinearScale()

scatt = Scatter(x=dates_all, y=sec2_levels, scales={'x': sc_x, 'y': sc_y}, default_colors=['dodgerblue'])
ax_x = Axis(scale=sc_x, label='Date')
ax_y = Axis(scale=sc_y, orientation='vertical', tick_format='0.2f', label='Security 2')

fig = Figure(marks=[scatt], axes=[ax_x, ax_y])
display(fig)

Changing the marker and adding text to each point of the scatter


In [ ]:
# Changing the marker as 
sc_x = LinearScale()
sc_y = LinearScale()

scatt = Scatter(x=x_data[:10], y=y_data[:10], names=np.arange(10),
                scales={'x': sc_x, 'y': sc_y}, default_colors=['red'], marker='cross')
ax_x = Axis(scale=sc_x)
ax_y = Axis(scale=sc_y, orientation='vertical', tick_format='0.2f')

fig = Figure(marks=[scatt], axes=[ax_x, ax_y], padding_x=0.025)
display(fig)

Changing the opacity of each marker


In [ ]:
scatt.default_opacities = [0.3, 0.5, 1.]

Representing additional dimensions of data

Linear Scale for Color Data


In [ ]:
sc_x = DateScale()
sc_y = LinearScale()

sc_c1 = ColorScale()
scatter = Scatter(x=dates_all, y=sec2_levels, color=sec1_returns,
                  scales={'x': sc_x, 'y': sc_y, 'color': sc_c1}, 
                  stroke='black')

ax_y = Axis(label='Security 2', scale=sc_y, 
            orientation='vertical', side='left')

ax_x = Axis(label='Date', scale=sc_x, num_ticks=10, label_location='end')
ax_c = ColorAxis(scale=sc_c1, tick_format='0.2%', label='Returns', orientation='vertical', side='right')

m_chart = dict(top=50, bottom=70, left=50, right=100)
fig = Figure(axes=[ax_x, ax_c, ax_y], marks=[scatter], fig_margin=m_chart, title='Scatter of Security 2 vs Dates')
display(fig)

In [ ]:
## Changing the default color. 
scatter.default_colors = ['blue']  # In this case, the dot with the highest X changes to blue.

In [ ]:
## setting the fill to be empty
scatter.stroke = None
scatter.fill = False

In [ ]:
## Setting the fill back
scatter.stroke = 'black'
scatter.fill = True

In [ ]:
## Changing the color to a different variable
scatter.color = sec2_levels
ax_c.tick_format = '0.0f'
ax_c.label = 'Security 2'

In [ ]:
## Changing the range of the color scale
sc_c1.colors = ['blue', 'green', 'orange']

Date Scale for Color Data


In [ ]:
sc_x = LinearScale()
sc_y = LinearScale()

sc_c1 = DateColorScale(scheme='Reds')
scatter = Scatter(x=sec2_levels, y=sec1_levels, color=dates_all,
                  scales={'x': sc_x, 'y': sc_y, 'color': sc_c1}, default_size=128,
                  stroke='black')

ax_y = Axis(label='Security 1 Level', scale=sc_y, orientation='vertical', side='left')

ax_x = Axis(label='Security 2', scale=sc_x)
ax_c = ColorAxis(scale=sc_c1, label='Date', num_ticks=10)

m_chart = dict(top=50, bottom=80, left=50, right=50)
fig = Figure(axes=[ax_x, ax_c, ax_y], marks=[scatter], fig_margin=m_chart)
display(fig)

Ordinal Scale for Color


In [ ]:
factor = int(np.ceil(len(sec2_levels) * 1.0 / len(ordinal_data)))
ordinal_data = np.tile(ordinal_data, factor)

c_ord = OrdinalColorScale(colors=['DodgerBlue', 'SeaGreen', 'Yellow', 'HotPink', 'OrangeRed'])
sc_x = LinearScale()
sc_y = LinearScale()

scatter2 = Scatter(x=sec2_levels[1:],
                   y=sec1_returns,
                   color=ordinal_data,
                   scales={'x': sc_x, 'y': sc_y, 'color': c_ord}, 
                   legend='__no_legend__',
                   stroke='black')

ax_y = Axis(label='Security 1 Returns', scale=sc_y, orientation='vertical', tick_format='.2%')

ax_x = Axis(label='Security 2', scale=sc_x, label_location='end')
ax_c = ColorAxis(scale=c_ord, label='Class', side='right', orientation='vertical')

m_chart = dict(top=50, bottom=70, left=100, right=100)
fig2 = Figure(axes=[ax_x, ax_y, ax_c], marks=[scatter2], fig_margin=m_chart)
display(fig2)

In [ ]:
ax_c.tick_format = "0.2f"
c_ord.colors = ['blue', 'red', 'green', 'yellow', 'orange']

Setting size and opacity based on data


In [ ]:
sc_x = LinearScale()
sc_y = LinearScale()
sc_y2 = LinearScale()

sc_size = LinearScale()
sc_opacity = LinearScale()


scatter2 = Scatter(x=sec2_levels[1:], y=sec1_levels, size=sec1_returns,
                   scales={'x': sc_x, 'y': sc_y, 'size': sc_size, 'opacity': sc_opacity}, 
                   default_size=128, default_colors=['orangered'], stroke='black')

ax_y = Axis(label='Security 1', scale=sc_y, orientation='vertical', side='left')
ax_x = Axis(label='Security 2', scale=sc_x)

fig2 = Figure(axes=[ax_x, ax_y], marks=[scatter2])
display(fig2)

In [ ]:
## Changing the opacity of the scatter
scatter2.default_opacities = [0.5, 0.3, 0.1]

In [ ]:
## Resetting the size for the scatter
scatter2.size=None

In [ ]:
## Resetting the opacity and setting the opacity according to the date
scatter2.default_opacities = [1.0]

In [ ]:
scatter2.opacity = dates_all

Changing the skew of the marker


In [ ]:
sc_x = LinearScale()
sc_y = LinearScale()
sc_e = LinearScale()

scatter = Scatter(scales={'x': sc_x, 'y': sc_y, 'skew': sc_e}, 
                  x=sec2_levels[1:], y=sec1_levels,
                  skew=sec1_returns, stroke="black",
                  default_colors=['gold'], default_size=200, 
                  marker='rectangle', default_skew=0)

ax_y = Axis(label='Security 1', scale=sc_y, orientation='vertical', side='left')
ax_x = Axis(label='Security 2', scale=sc_x)

Figure(axes=[ax_x, ax_y], marks=[scatter], animation_duration=1000)

In [ ]:
scatter.skew = None

In [ ]:
scatter.skew = sec1_returns

Rotation scale


In [ ]:
sc_x = LinearScale()
sc_y = LinearScale()
sc_e = LinearScale()
sc_c = ColorScale(scheme='Reds')
x1 = np.linspace(-1, 1, 30)
y1 = np.linspace(-1, 1, 30)
x, y = np.meshgrid(x1,y1)
x, y = x.flatten(), y.flatten()
rot = x**2 + y**2
color=x-y
scatter = Scatter(scales={'x': sc_x, 'y': sc_y, 'color': sc_c, 'rotation': sc_e}, 
                  x=x, y=y, rotation=rot, color=color,
                  stroke="black", default_size=200, 
                  marker='arrow', default_skew=0.5,)
Figure(marks=[scatter], animation_duration=1000)

In [ ]:
scatter.rotation = 1.0 / (x ** 2 + y ** 2 + 1)

Scatter Chart Interactions

Moving points in Scatter


In [ ]:
## Enabling moving of points in scatter. Try to click and drag any of the points in the scatter and 
## notice the line representing the mean of the data update
sc_x = LinearScale()
sc_y = LinearScale()

scat = Scatter(x=x_data[:10], y=y_data[:10], scales={'x': sc_x, 'y': sc_y}, default_colors=['orange'],
               enable_move=True)
lin = Lines(scales={'x': sc_x, 'y': sc_y}, line_style='dotted', colors=['orange'])

def update_line(change=None):
    with lin.hold_sync():
        lin.x = [np.min(scat.x), np.max(scat.x)]
        lin.y = [np.mean(scat.y), np.mean(scat.y)]

update_line()
# update line on change of x or y of scatter
scat.observe(update_line, names=['x'])
scat.observe(update_line, names=['y'])

ax_x = Axis(scale=sc_x)
ax_y = Axis(scale=sc_y, tick_format='0.2f', orientation='vertical')

fig = Figure(marks=[scat, lin], axes=[ax_x, ax_y])
display(fig)

In [ ]:
latex_widget = Label(color='Green', font_size='16px')

def callback_help(name, value):
    latex_widget.value = str(value)
    
latex_widget

In [ ]:
scat.on_drag_start(callback_help)

In [ ]:
scat.on_drag(callback_help)

In [ ]:
scat.on_drag_end(callback_help)

In [ ]:
## Restricting movement to only along the Y-axis
scat.restrict_y = True

Adding points to Scatter


In [ ]:
## Enabling adding the points to Scatter. Try clicking anywhere on the scatter to add points
with scat.hold_sync():
    scat.enable_move = False
    scat.interactions = {'click': 'add'}

Updating X and Y while moving the point


In [ ]:
## In this case on drag, the line updates as you move the points.
with scat.hold_sync():
    scat.enable_move = True
    scat.update_on_move = True
    scat.interactions = {'click': None}

Custom event on end of drag


In [ ]:
## Whenever drag is ended, there is a custom event dispatched which can be listened to.
## try dragging a point and see the data associated with the event being printed
def test_func(self, content):
    print("received drag end", content)

scat.on_drag_end(test_func)

Adding tooltip


In [ ]:
x_sc = LinearScale()
y_sc = LinearScale()

x_data = x_data[:50]
y_data = y_data[:50]

def_tt = Tooltip(fields=['x', 'y'], formats=['', '.2f'])

scatter_chart = Scatter(x=x_data, y=y_data, scales= {'x': x_sc, 'y': y_sc}, default_colors=['dodgerblue'],
                        tooltip=def_tt)
ax_x = Axis(scale=x_sc)
ax_y = Axis(scale=y_sc, orientation='vertical', tick_format='0.2f')

fig = Figure(marks=[scatter_chart], axes=[ax_x, ax_y])
display(fig)

In [ ]:
## removing field names from the tooltip
def_tt.show_names = False

In [ ]:
## changing the fields displayed in the tooltip
def_tt.fields = ['y']