Lasso deselect from inside a function


#1

Hi there,

I am trying to use the lasso to select data on one plot and show in in the other plot via indices and vice versa, however I cannot make it work since the data of the first plot stays selected. Does anyone know how to call a lasso (or range) deselect from a function?

here an attemp to make it work:


import numpy as np
import plotly.graph_objs as go
import ipywidgets as widgets
from plotly.colors import DEFAULT_PLOTLY_COLORS

x = np.linspace(0,6,100)
y = np.sin(x)

data = [go.Scattergl(x=x, y=y, mode='markers', name = 'All nodes', selected=dict(marker=dict(color=DEFAULT_PLOTLY_COLORS[1]))),
        go.Scattergl(mode='markers', name='selected nodes')]

layout = dict(title=dict(text = 'plot1'), dragmode='lasso')
fig = go.FigureWidget(data=data, layout= layout)

data2 = [go.Scattergl(x=y, y=x, mode='markers', name = 'All nodes', selected=dict(marker=dict(color=DEFAULT_PLOTLY_COLORS[1]))),
        go.Scattergl(mode='markers', name='selected nodes')]

layout2 = dict(title=dict(text = 'plot2'), dragmode='lasso')
fig2 = go.FigureWidget(data=data2, layout= layout2)

def selection_fn(trace, points, selector):
    # !!!! here call deselect lasso from plot2
    fig.data[1].x = [None]
    fig.data[1].y = [None]
    fig.layout.xaxis.autorange = True
    fig2.data[1].x = y[points.point_inds]
    fig2.data[1].y = x[points.point_inds]
    
    
def selection_fn2(trace, points, selector):
    # !!!! here call deselect lasso from plot1
    fig2.data[1].x = [None]
    fig2.data[1].y = [None]
    fig.data[1].x = x[points.point_inds]
    fig.data[1].y = y[points.point_inds]


fig.data[0].on_selection(selection_fn)
fig2.data[0].on_selection(selection_fn2)
display(widgets.HBox([fig,fig2]))

I made this as an alternative but it was not really what i aimed to do:

import numpy as np
import plotly.graph_objs as go
import ipywidgets as widgets
from plotly.colors import DEFAULT_PLOTLY_COLORS

x = np.linspace(0,6,100)
y = np.sin(x)

data = [go.Scattergl(x=x, y=y, mode='markers', name = 'All nodes', marker=dict(color=np.array([DEFAULT_PLOTLY_COLORS[0]]*len(x))),
                     selected=dict(marker=dict(color=DEFAULT_PLOTLY_COLORS[1])))]

layout = dict(title=dict(text = 'plot1'), dragmode='lasso')
fig = go.FigureWidget(data=data, layout= layout)

data2 = [go.Scattergl(x=y, y=x, mode='markers', name = 'All nodes', marker=dict(color=np.array([DEFAULT_PLOTLY_COLORS[0]]*len(x))),
                      selected=dict(marker=dict(color=DEFAULT_PLOTLY_COLORS[2])))]

layout2 = dict(title=dict(text = 'plot2'), dragmode='lasso')
fig2 = go.FigureWidget(data=data2, layout= layout2)

def selection_fn(trace, points, selector):
    col = np.array([DEFAULT_PLOTLY_COLORS[0]]*len(x))
    col[points.point_inds] = np.array([DEFAULT_PLOTLY_COLORS[1]]*len(points.point_inds))
    fig2.data[0].marker.color = col
    
def selection_fn2(trace, points, selector):
    col = np.array([DEFAULT_PLOTLY_COLORS[0]]*len(x))
    col[points.point_inds] = np.array([DEFAULT_PLOTLY_COLORS[2]]*len(points.point_inds))
    fig.data[0].marker.color = col

fig.data[0].on_selection(selection_fn)
fig2.data[0].on_selection(selection_fn2)
display(widgets.HBox([fig,fig2]))

#2

Hi @Alexboiboi,

You can use the scatter.selectedpoints property to control which points are selected programmatically. Here’s an example:

import numpy as np
import plotly.graph_objs as go
import ipywidgets as widgets
from plotly.colors import DEFAULT_PLOTLY_COLORS

x = np.linspace(0,6,100)
y = np.sin(x)

data = [go.Scattergl(x=x, y=y, mode='markers', name = 'All nodes',
                     selected=dict(marker=dict(color=DEFAULT_PLOTLY_COLORS[1])))]

layout = dict(title=dict(text = 'plot1'), dragmode='lasso')
fig = go.FigureWidget(data=data, layout= layout)

data2 = [go.Scattergl(x=y, y=x, mode='markers', name = 'All nodes',
                      selected=dict(marker=dict(color=DEFAULT_PLOTLY_COLORS[1])))]

layout2 = dict(title=dict(text = 'plot2'), dragmode='lasso')
fig2 = go.FigureWidget(data=data2, layout= layout2)

def selection_fn(trace, points, selector):
    fig.data[0].selectedpoints = None
    fig.data[0].selectedpoints = []
    fig2.data[0].selectedpoints = points.point_inds 

    
def selection_fn2(trace, points, selector):
    fig2.data[0].selectedpoints = None    
    fig2.data[0].selectedpoints = []
    fig.data[0].selectedpoints = points.point_inds


fig.data[0].on_selection(selection_fn)
fig2.data[0].on_selection(selection_fn2)
display(widgets.HBox([fig,fig2]))

Does this do what you want? Note that I set the selectedpoints property to None and then to []. This was to work around a problem I was seeing where the selectedpoints update would only be processed the first time the callback was called. I think it has something to do with the order in which the selectedpoints property is updated and the when the on_selection callback is called.

Hope that helps!

-Jon


#3

Hi Jon, thanks a lot for your answer, this is exactly what I needed. You are the best!

I was also wondering why the selectedpoints update was only performed only once!


#4

Great!

I also opened an issue about the selectedpoints issue at https://github.com/plotly/plotly.py/issues/1433. So hopefully we can get rid of the need to perform the None assignment.

-Jon


#5

Hi, Could I ask you how to display two diagrams in one html file as in your diagram?


#6

Hi @visual,

The easiest thing to do here is to use subplots: https://plot.ly/python/subplots/, and then save the figure to HTML with plotly.offline.plot If that doesn’t work for you, you could use an approach like I describe in How to make Html Report readable from all browsers to generate a custom HTML file with multiple figures.

Hope that helps!
-Jon