Ipywidgets and plotly interactions

I’m trying to make an interactive plot with ipywidgets using plotly, but I’m afraid i’m not getting something. I have some dataframe with coordinates and some columns. I’d want to plot the dataframe in a scatterplot so that coord1=x, coord2=y and each marker point is colored by the value of a column selected by a column selected interactively.Additionally I’d want that when I change the column value with the interactive menu, the color for every point changes to the column that i selected, rescaling the min and max of the colorbar accordingly to the min and max of the new column. Furthermore, when I change another selector (selector2) then i want the plot to display only the subset of mu dataframe that matched a certain colID big_grid[big_grid[“id_col”]==selector2.value]. Lastly there should be a rangeslider widget to adjust the color range of the colorbar.
I can kinda do it defining a plot function and then calling widgets.interact(plot_function) as in this code:

def plot(elem,rang):
    fig = px.scatter(subset, x="coord1", y="coord2", color=elem,color_continuous_scale="Turbo",range_color=rang)
    fig.show()

def handle_elem_change(change):
    with rangewidg.hold_trait_notifications():    #This is because if you do't put it it set max, 

        rangewidg.max=big_grid[dropm_elem.value].max() #and if max is < min he freaks out. Like this he first
        rangewidg.min=big_grid[dropm_elem.value].min() #set everything and then send the eventual errors notification.
        rangewidg.value=[big_grid[dropm_elem.value].min(),big_grid[dropm_elem.value].max()]

def handle_id_change(change):
    global subset
    subset=big_grid[big_grid['id_col'].isin(dropm_id.value)]

big_grid=pd.DataFrame(data=dict(id_col=[1,2,3,4,5],
                         col1=[0.1,0.2,0.3,0.4,0.5],
                         col2=[10,20,30,40,50],
                         coord1=[6,7,8,9,10],
                         coord2=[6,7,8,9,10]))
subset=big_grid
list_elem=["col1","col2"]
list_id=big_grid.id_col.values

dropm_elem=widgets.Dropdown(options=list(list_elem)) 
dropm_id=widgets.SelectMultiple(
                options=list_id,
                description="Active",
                disabled=False
) 

rangewidg=widgets.FloatRangeSlider(value=[big_grid[dropm_elem.value].min(),big_grid[dropm_elem.value].max()],
                               min=big_grid[dropm_elem.value].min(),
                               max=big_grid[dropm_elem.value].max(),
                               step=0.001,
                               readout_format='.3f',
                               description="Color Range",
                              continuous_update=False)


dropm_elem.observe(handle_elem_change,names='value')
dropm_id.observe(handle_id_change,names='value')

display(dropm_id)
widgets.interact(plot,elem=dropm_elem,rang=rangewidg) 

The problem is that this is not a widget plot, it’s just a plot managed by widgets. I cannot get how to reproduce the same behaviour making use of the go.FigureWidget() function. I tried something like this:

big_grid=pd.DataFrame(data=dict(id_col=[1,2,3,4,5],
                         col1=[0.1,0.2,0.3,0.4,0.5],
                         col2=[10,20,30,40,50],
                         coord1=[6,7,8,9,10],
                         coord2=[6,7,8,9,10]))
list_elem=["col1","col2"]
list_id=big_grid.id_col.values

dropm_elem=widgets.Dropdown(options=list(list_elem)) 
dropm_id=widgets.SelectMultiple(
                options=list_id,
                description="Active",
                disabled=False
) 

rangewidg=widgets.FloatRangeSlider(value=[big_grid[dropm_elem.value].min(),big_grid[dropm_elem.value].max()],
                               min=big_grid[dropm_elem.value].min(),
                               max=big_grid[dropm_elem.value].max(),
                               step=0.001,
                               readout_format='.3f',
                               description="Color Range",
                              continuous_update=False)


fig = go.FigureWidget(data=px.scatter(big_grid, 
                                      x="coord1",
                                      y="coord2",
                                      color=big_grid[dropm_elem.value],
                                      color_continuous_scale="Turbo",)
                         )
def handle_id_change(change):
    fig.data[0]['x']=big_grid[big_grid['id_col'].isin(dropm_id.value)]["coord1"]
    fig.data[0]['y']=big_grid[big_grid['id_col'].isin(dropm_id.value)]["coord2"]
    fig.data[0]['marker']['color']=big_grid[big_grid['id_col'].isin(dropm_id.value)][dropm_elem.value]
    fig.data[0]['marker']['cmin']=big_grid[big_grid['id_col'].isin(dropm_id.value)][dropm_elem.value].min()
    fig.data[0]['marker']['cmax']=big_grid[big_grid['id_col'].isin(dropm_id.value)][dropm_elem.value].max()
    
def handle_elem_change(change):
    fig.data[0]['marker']['color']=big_grid[big_grid['id_col'].isin(dropm_id.value)][dropm_elem.value]   
   
dropm_elem.observe(handle_elem_change,names='value')
dropm_id.observe(handle_id_change,names='value')


right_box1 =widgets.HBox([fig])
right_box2=widgets.VBox([dropm_elem,dropm_id,rangewidg])
box=widgets.HBox([right_box1,right_box2])
box

but the rangewidget and the hovering are broken. Basically when i change dromp_elem the color doesn’t adjust as i am expecting, and instead it gets dark and uniform. At the same time if you change column and you hover over the points it lists the value of col2, but the label still says col1.

I’m afraid that I’m overcomplicating my life and there is surely an easier way, could someone enlighten me?

Hi there,

your 2nd example is working as expected, you just probably forgot to define the callback for the range widget.

Also, if you are working with jupyterlab, make sure you are working with the latest version of jupyterlab (at least 3.0) and plotly (at least 5.0 because of the prebuild jupyterlab extension)