Transform on_change into JS callbacks

Hello,

I’m not very familiar with JAVA and I’m having some problems writing this program as ‘.html’.
This small example shows the following functionality:

Imports:

import pandas as pd
import numpy as np
import random

from bokeh.models import (ColumnDataSource,
Toggle,CustomJS,HoverTool, Select,
Button, TextInput, LabelSet,
CheckboxGroup, DataTable, TableColumn
)

from bokeh.plotting import figure, curdoc, show
from bokeh.layouts import column, row

Data:

x = random.sample(range(10, 30), 10)
y = random.sample(range(40, 70), 10)
z = random.sample(range(70, 100),10)
l = [‘P’+str(n) for n in range(len(z))]
c = [‘skyblue’]*len(x)
cl= [‘c1’]*4 + [‘c2’]*3 + [‘c3’]*3

dataf = pd.DataFrame({‘Var1’: x, ‘Var2’: y, ‘Var3’: z, ‘Label’: l, ‘color’: c, ‘class’: cl} )

Predefined issues:

HOVER_TOOLTIPS = HoverTool(tooltips=
[(“Label”, “@l”),
(‘Var-1’, ‘@x’ ),
(‘Var-2’, ‘@y’ ),
(‘Var-3’, ‘@z’ ),
])

axis_map = {
“Variable-1”: “Var1”,
“Variable-2”: “Var2”,
“Variable-3”: “Var3”
}

class_map = {
‘Class-1’: ‘c1’,
‘Class-2’: ‘c2’,
‘Class-3’: ‘c3’,
}

Code:

source = ColumnDataSource(data=dict(x=,
y=,
z=,
l=,
c=
))

p1 = figure(plot_width=800, plot_height=700,
tools=[HOVER_TOOLTIPS,
“tap,pan,lasso_select,wheel_zoom,save,reset”],
toolbar_location=“above”,
)

p1.circle(x=“x”, y=“y”,color=‘c’ ,fill_alpha=0.8 ,source=source, size=10,
selection_line_color=“darkblue”)

Labels button:

labels = LabelSet(x=‘x’, y=‘y’, text=‘l’,
x_offset=5, y_offset=5, source=source, render_mode=‘canvas’,visible=False)
p1.add_layout(labels)
button = Toggle(label=‘Labels’, button_type=“primary”, active=False)

Table:

columns = [TableColumn(field=n,title=n) for n in [‘Var1’,‘Var2’,‘Var3’,‘Label’]]
table = DataTable(source=source,columns=columns, width=800, height=300)

Axis:

x_axis = Select(title=“X Axis”, options=sorted(axis_map.keys()), value=“Variable-1”)
y_axis = Select(title=“Y Axis”, options=sorted(axis_map.keys()), value=“Variable-2”)

Finder:

finder = TextInput(title=“Finder”)

Classes:

class_selection = CheckboxGroup(labels=list(class_map.keys()), active = )

def select_pulsar():

selected = dataf.copy()
    
class_to_paint = [class_map[class_selection.labels[i]] for i in class_selection.active]
for name in class_to_paint:
    if name == 'c1':
        selected.loc[selected['class'] == 'c1','color'] = 'orange'
    
    if name == 'c2':
        selected.loc[selected['class'] == 'c2','color'] = 'yellow' 

    if name == 'c3':
        selected.loc[selected['class'] == 'c3','color'] = 'green' 
        
finder_value = finder.value.strip()
if (finder_value != ""):
    selected.loc[selected.Label==finder_value,'color'] = 'red'
        
return selected

def update():
df = select_pulsar()
update_table(df)

x_var = axis_map[x_axis.value]
y_var = axis_map[y_axis.value]

p1.xaxis.axis_label = x_axis.value
p1.yaxis.axis_label = y_axis.value

source.data = dict(
    x=df[x_var],
    y=df[y_var],
    z=df['Var3'],
    l=df['Label'],
    c=df['color']
)

def update_table(data):
table.source = ColumnDataSource(data)

def selection_change(attrname, old, new):
data = dataf.copy()
selected = source.selected.indices
if selected:
data = data.iloc[selected, :]

update_table(data)

Callbacks:

source.selected.on_change(‘indices’, selection_change) #Selected pulsars

button.js_link(‘active’, labels, ‘visible’) #Button

class_selection.on_change(‘active’, lambda attr, old, new: update()) #Classes

FINDERS = [x_axis, y_axis, finder] axis and finder

for control in FINDERS:
control.on_change(‘value’, lambda attr, old, new: update())

Display:

BUTTONS = [button,x_axis, y_axis, finder, class_selection]

inputs = column(*BUTTONS, width=220)

display = column(row(inputs, column(p1,table)))

update() # Initial loading

curdoc().add_root(display)

  • Display the labels associated with each value using ‘Toggle’.
  • Display in the table the values selected in the figure with both ‘tap’ and ‘lasso select’.
  • Change the variables that define the axes with ‘Select’.
  • Indicate in another color the value determined in ‘TextInput’ by introducing the label associated with each value. This is defined in the data frame as ‘Label’.
  • Change the color of the values according to their class through ‘CheckboxGroup’. This is defined in the data frame as ‘class’.

The ‘Toggle’ functionality has been easy for me to describe but I’m quite lost with the rest.

Any idea how to transform these functionalities by avoiding ‘.on_change’?

Thanks a lot for the help and attention.[quote=“Charlie, post:1, topic:9148, full:true”]
Hello,

I’m not very familiar with JAVA and I’m having some problems writing this program as ‘.html’.
This small example shows the following functionality:

import pandas as pd
import numpy as np
import random

from bokeh.models import (ColumnDataSource,
Toggle,CustomJS,HoverTool, Select,
Button, TextInput, LabelSet,
CheckboxGroup, DataTable, TableColumn
)

from bokeh.plotting import figure, curdoc, show
from bokeh.layouts import column, row

x = random.sample(range(10, 30), 10)
y = random.sample(range(40, 70), 10)
z = random.sample(range(70, 100),10)
l = [‘P’+str(n) for n in range(len(z))]
c = [‘skyblue’]*len(x)
cl= [‘c1’]*4 + [‘c2’]*3 + [‘c3’]*3

dataf = pd.DataFrame({‘Var1’: x, ‘Var2’: y, ‘Var3’: z, ‘Label’: l, ‘color’: c, ‘class’: cl} )

HOVER_TOOLTIPS = HoverTool(tooltips=
[(“Label”, “@l”),
(‘Var-1’, ‘@x’ ),
(‘Var-2’, ‘@y’ ),
(‘Var-3’, ‘@z’ ),
])

axis_map = {
“Variable-1”: “Var1”,
“Variable-2”: “Var2”,
“Variable-3”: “Var3”
}

class_map = {
‘Class-1’: ‘c1’,
‘Class-2’: ‘c2’,
‘Class-3’: ‘c3’,
}

source = ColumnDataSource(data=dict(x=,
y=,
z=,
l=,
c=
))

p1 = figure(plot_width=800, plot_height=700,
tools=[HOVER_TOOLTIPS,
“tap,pan,lasso_select,wheel_zoom,save,reset”],
toolbar_location=“above”,
)

p1.circle(x=“x”, y=“y”,color=‘c’ ,fill_alpha=0.8 ,source=source, size=10,
selection_line_color=“darkblue”)

labels = LabelSet(x=‘x’, y=‘y’, text=‘l’,
x_offset=5, y_offset=5, source=source, render_mode=‘canvas’,visible=False)
p1.add_layout(labels)
button = Toggle(label=‘Labels’, button_type=“primary”, active=False)

columns = [TableColumn(field=n,title=n) for n in [‘Var1’,‘Var2’,‘Var3’,‘Label’]]

table = DataTable(source=source,columns=columns, width=800, height=300)

x_axis = Select(title=“X Axis”, options=sorted(axis_map.keys()), value=“Variable-1”)
y_axis = Select(title=“Y Axis”, options=sorted(axis_map.keys()), value=“Variable-2”)

finder = TextInput(title=“Finder”)

class_selection = CheckboxGroup(labels=list(class_map.keys()), active = )

def select_pulsar():

selected = dataf.copy()
    
class_to_paint = [class_map[class_selection.labels[i]] for i in class_selection.active]
for name in class_to_paint:
    if name == 'c1':
        selected.loc[selected['class'] == 'c1','color'] = 'orange'
    
    if name == 'c2':
        selected.loc[selected['class'] == 'c2','color'] = 'yellow' 

    if name == 'c3':
        selected.loc[selected['class'] == 'c3','color'] = 'green' 
        
finder_value = finder.value.strip()
if (finder_value != ""):
    selected.loc[selected.Label==finder_value,'color'] = 'red'
        
return selected

def update():
df = select_pulsar()
update_table(df)

x_var = axis_map[x_axis.value]
y_var = axis_map[y_axis.value]

p1.xaxis.axis_label = x_axis.value
p1.yaxis.axis_label = y_axis.value

source.data = dict(
    x=df[x_var],
    y=df[y_var],
    z=df['Var3'],
    l=df['Label'],
    c=df['color']
)

def update_table(data):
table.source = ColumnDataSource(data)

def selection_change(attrname, old, new):
data = dataf.copy()
selected = source.selected.indices
if selected:
data = data.iloc[selected, :]

update_table(data)

source.selected.on_change(‘indices’, selection_change)

button.js_link(‘active’, labels, ‘visible’)

class_selection.on_change(‘active’, lambda attr, old, new: update())

FINDERS = [x_axis, y_axis, finder] axis and finder

for control in FINDERS:
control.on_change(‘value’, lambda attr, old, new: update())

BUTTONS = [button,x_axis, y_axis, finder, class_selection]

inputs = column(*BUTTONS, width=220)

display = column(row(inputs, column(p1,table)))

update()

curdoc().add_root(display)

  • Display the labels associated with each value using ‘Toggle’.
  • Display in the table the values selected in the figure with both ‘tap’ and ‘lasso select’.
  • Change the variables that define the axes with ‘Select’.
  • Indicate in another color the value determined in ‘TextInput’ by introducing the label associated with each value. This is defined in the data frame as ‘Label’.
  • Change the color of the values according to their class through ‘CheckboxGroup’. This is defined in the data frame as ‘class’.

The ‘Toggle’ functionality has been easy for me to describe but I’m quite lost with the rest.

Any idea how to transform these functionalities by avoiding ‘.on_change’?

I have tried to paste the code as best as possible, any advice on how to do this task better is welcome. Apologies in advance if it is not entirely intelligible.

Thanks a lot for the help and attention.

Hi @Charlie please edit your post to use code formatting so that the code is intelligible (either with the </> icon on the editing toolbar, or triple backtick ``` fences around the code blocks)

Hi, sorry for the previous format. I hope it reads better now.

import pandas as pd
import numpy as np
import random

from bokeh.models import (ColumnDataSource, 
                          Toggle,CustomJS,HoverTool, Select, 
                          Button, TextInput, LabelSet,
                          CheckboxGroup,  DataTable, TableColumn
                         )

from bokeh.plotting import figure, curdoc, show
from bokeh.layouts import column, row



## Data:

x = random.sample(range(10, 30), 10)
y = random.sample(range(40, 70), 10)
z = random.sample(range(70, 100),10)
l = ['P'+str(n) for n in range(len(z))]
c = ['skyblue']*len(x)
cl= ['c1']*4 + ['c2']*3 + ['c3']*3

dataf = pd.DataFrame({'Var1': x, 'Var2': y, 'Var3': z, 'Label': l, 'color': c, 'class': cl} )

HOVER_TOOLTIPS = HoverTool(tooltips=
                 [("Label",      "@l"),
                  ('Var-1',      '@x' ),
                  ('Var-2',      '@y' ),
                  ('Var-3',      '@z' ),
                 ])

axis_map = {
    "Variable-1": "Var1",
    "Variable-2": "Var2",
    "Variable-3": "Var3"
}

class_map = {
    'Class-1': 'c1',
    'Class-2': 'c2',
    'Class-3': 'c3',
}

source = ColumnDataSource(data=dict(x=[],
                                    y=[],
                                    z=[],
                                    l=[],
                                    c=[]
                                   ))

p1 = figure(plot_width=800, plot_height=700,
            tools=[HOVER_TOOLTIPS,
                   "tap,pan,lasso_select,wheel_zoom,save,reset"],
            toolbar_location="above",
           )


p1.circle(x="x", y="y",color='c' ,fill_alpha=0.8 ,source=source, size=10,
           selection_line_color="darkblue")

##################################################################################


# Labels button:
labels = LabelSet(x='x', y='y', text='l',
              x_offset=5, y_offset=5, source=source, render_mode='canvas',visible=False)
p1.add_layout(labels)
button = Toggle(label='Labels', button_type="primary", active=False)

# Table:
columns = [TableColumn(field=n,title=n) for n in ['Var1','Var2','Var3','Label']]
table = DataTable(source=source,columns=columns, width=800, height=300)

# Axis:
x_axis = Select(title="X Axis", options=sorted(axis_map.keys()), value="Variable-1")
y_axis = Select(title="Y Axis", options=sorted(axis_map.keys()), value="Variable-2")

# Finder:
finder = TextInput(title="Finder")

# Classes:
class_selection = CheckboxGroup(labels=list(class_map.keys()), active = [])


def select_pulsar():

    selected = dataf.copy()
        
    class_to_paint = [class_map[class_selection.labels[i]] for i in class_selection.active]
    for name in class_to_paint:
        if name == 'c1':
            selected.loc[selected['class'] == 'c1','color'] = 'orange'
        
        if name == 'c2':
            selected.loc[selected['class'] == 'c2','color'] = 'yellow' 

        if name == 'c3':
            selected.loc[selected['class'] == 'c3','color'] = 'green' 
            
    finder_value = finder.value.strip()
    if (finder_value != ""):
        selected.loc[selected.Label==finder_value,'color'] = 'red'
            
    return selected


def update():
    df = select_pulsar()
    update_table(df) 

    x_var = axis_map[x_axis.value]
    y_var = axis_map[y_axis.value]
    
    p1.xaxis.axis_label = x_axis.value
    p1.yaxis.axis_label = y_axis.value
    
    source.data = dict(
        x=df[x_var],
        y=df[y_var],
        z=df['Var3'],
        l=df['Label'],
        c=df['color']
    )
    
def update_table(data):
    table.source = ColumnDataSource(data)
    
        
def selection_change(attrname, old, new):
    data = dataf.copy()
    selected = source.selected.indices
    if selected:
        data = data.iloc[selected, :]
        
    update_table(data)

### Callbacks:

source.selected.on_change('indices', selection_change) #Selected pulsars


button.js_link('active', labels, 'visible') #Button


class_selection.on_change('active', lambda attr, old, new: update()) #Classes

FINDERS = [x_axis, y_axis, finder] #Axis and finder

for control in FINDERS:
    control.on_change('value', lambda attr, old, new: update())
    
    
### Display:
BUTTONS = [button,x_axis, y_axis, finder, class_selection]

inputs = column(*BUTTONS, width=220)

display = column(row(inputs, column(p1,table)))



update()  # Initial loading 


curdoc().add_root(display)



As I commented the above, I have hardly worked in Java and I have some problems seeing how to adapt functionalities developed only with purely python callbacks to Java format.
I have some ideas but I’m still not clear on how to do them.

Any help is welcome but anyway, thank you very much for everything.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.