Dual Yaxis on Python Mouse Selection Event

Hi All!

I’ve been trying to add dual yaxis to a slightly modified (timeseries form) IpyWidgets sample without much success. I tried adding additional columns as a dictionary i.e. y = df[[‘Classification’, ‘City mpg’]], yaxis=‘y2’ as well as a separate Trace. I’ve been modifying the code below; however, the best thing i get is an overlapping yaxis with the 2nd variable. I would appreciate any feedback/help. Thanks!

import plotly.graph_objs as go
import plotly.offline as py

import pandas as pd
import numpy as np
from ipywidgets import interactive, HBox, VBox

py.init_notebook_mode()

df = pd.read_csv('https://raw.githubusercontent.com/jonmmease/plotly_ipywidget_notebooks/master/notebooks/data/cars/cars.csv')
df['Date'] = pd.date_range(start='1/1/1979', periods=len(df), freq='D')

f = go.FigureWidget([go.Scatter(y = df['City mpg'], x = df['Date'], mode = 'markers'),
                go.Scatter(y = df['Torque'], x = df['Date'], mode = 'markers', yaxis='y2')])
scatter = f.data[0]
N = len(df)
scatter.x = df['Date']
scatter.y = scatter.y + np.random.rand(N)/10 *(df['City mpg'].max() - df['City mpg'].min())
scatter.marker.opacity = 0.5

def update_axes(xaxis, yaxis):
    scatter = f.data[0]
    scatter.x = df[xaxis]
    scatter.y = df[yaxis]
    with f.batch_update():
        f.layout.xaxis.title = xaxis
        f.layout.yaxis.title = yaxis
        scatter.x = scatter.x + np.random.rand(N)/10 *(df[xaxis].max() - df[xaxis].min())
        scatter.y = scatter.y + np.random.rand(N)/10 *(df[yaxis].max() - df[yaxis].min())

t = go.FigureWidget([go.Table(
    header=dict(values=['ID','Classification','Driveline','Hybrid'],
                fill = dict(color='#C2D4FF'),
                align = ['left'] * 5),
    cells=dict(values=[df[col] for col in ['ID','Classification','Driveline','Hybrid']],
                fill = dict(color='#F5F8FF'),
               align = ['left'] * 5))])

def selection_fn(trace,points,selector):
    t.data[0].cells.values = [df.loc[points.point_inds][col] for col in ['ID','Classification','Driveline','Hybrid']]

scatter.on_selection(selection_fn)

# Put everything together
VBox((HBox(),f,t))

Hi @mini_geek,

Here’s what you need in order to position the second y-axis on the right and to show the data associated with both axes.

layout = go.Layout(
        yaxis2=go.layout.YAxis(anchor='x', overlaying='y', side='right')
)

Here’s the full example, where I also changed your scatter traces to scattergl for better performance.

import plotly.graph_objs as go
import plotly.offline as py

import pandas as pd
import numpy as np
from ipywidgets import interactive, HBox, VBox

py.init_notebook_mode()

df = pd.read_csv('https://raw.githubusercontent.com/jonmmease/plotly_ipywidget_notebooks/master/notebooks/data/cars/cars.csv')
df['Date'] = pd.date_range(start='1/1/1979', periods=len(df), freq='D')

f = go.FigureWidget(
    data = [go.Scattergl(y = df['City mpg'], x = df['Date'], mode = 'markers'),
            go.Scattergl(y = df['Torque'], x = df['Date'], mode = 'markers', yaxis='y2')],
    layout = go.Layout(
        yaxis2=go.layout.YAxis(anchor='x', overlaying='y', side='right')
    )
)
scatter = f.data[0]
N = len(df)
scatter.x = df['Date']
scatter.y = scatter.y + np.random.rand(N)/10 *(df['City mpg'].max() - df['City mpg'].min())
scatter.marker.opacity = 0.5

def update_axes(xaxis, yaxis):
    scatter = f.data[0]
    scatter.x = df[xaxis]
    scatter.y = df[yaxis]
    with f.batch_update():
        f.layout.xaxis.title = xaxis
        f.layout.yaxis.title = yaxis
        scatter.x = scatter.x + np.random.rand(N)/10 *(df[xaxis].max() - df[xaxis].min())
        scatter.y = scatter.y + np.random.rand(N)/10 *(df[yaxis].max() - df[yaxis].min())

t = go.FigureWidget([go.Table(
    header=dict(values=['ID','Classification','Driveline','Hybrid'],
                fill = dict(color='#C2D4FF'),
                align = ['left'] * 5),
    cells=dict(values=[df[col] for col in ['ID','Classification','Driveline','Hybrid']],
                fill = dict(color='#F5F8FF'),
               align = ['left'] * 5))])

def selection_fn(trace,points,selector):
    t.data[0].cells.values = [df.loc[points.point_inds][col] for col in ['ID','Classification','Driveline','Hybrid']]

scatter.on_selection(selection_fn)

# Put everything together
VBox((HBox(),f,t))


Hope that helps!
-Jon

1 Like

Awesome! thanks @jmmease, that does it.
I tried the layout function, but i thought i had to use the figure and py or pyo.iplot(fig) which was generating an error.

fig = dict(data=data, layout=layout)
pyo.iplot(fig)

On another note -this question may be a bit out-there,- is it possible to combine bqplot functions (such as brushintsel interaction) with widgets on the same graph?

Hi @mini_geek,

is it possible to combine bqplot functions (such as brushintsel interaction) with widgets on the same graph?

You can put plotly FigureWidget figures alongside bqplot figures in the same layout by using ipywidget layout containers (e.g. VBox, HBox). But I’m not sure what you mean by combining bqplot functions.

-Jon

Hi @jmmease,
Thanks for the tip.

I meant something similar to the previous example and using the bruhintsel interaction instead of the lasso to select data points and have them display on a table.

You won’t b e able to select points in the plotly figure using the bqplot selection tool. But you could put a bqplot plot next to a plotly table, and use a callback on the bqplot selection to update the plotly table.
-Jon

Great @jmmease, i’ll give it a go.