Dynamically update extra_y_ranges or extra_x_ranges

Hi,
I ran into an issue on how to update extra_y_ranges as the new x or y axis is selected, and I could use your help.

Short description: I’m building dashboard that display data with large range of values, so in order to have nice hexbin display, I normalized the data (in range 0, 1) - otherwise I wasn’t able to make haxbin display with x axis values of 0-8 while y axis is 10e5-10e15. However, I would like to have on extra y and x axis the real values (or if possible overwrite the current ones). I tried manually setting yaxis.major_label_overrides however that doesn’t work with zoom (as it disappears), and I can only define max value.

My other approach was with extra range, however, the Figure disappears and doesn’t updates when I select new x/y axis. Can you help me with placing extra_y_ranges in order to update Figure and the other y_axis on selecting new x/y values?

    p.extra_y_ranges = {"yraw": Range1d(start=min(data[new]),
                                end=max(data[new]),
                                )}
    p.add_layout(LinearAxis(y_range_name="yraw"), 'right') 

Minimum executive code with one dropdown menu and ranges are in the data_bin_change function:

import numpy as np

from bokeh.io import curdoc
from bokeh.layouts import column, row
from bokeh.models import ColorBar, LogTicker, HoverTool, ColumnDataSource, Select
from bokeh.plotting import figure
from bokeh.transform import log_cmap
from bokeh.util.hex import hexbin
from bokeh.models import Range1d, LinearAxis


data = {'x': np.random.randint(200, size=50),
        'y': np.random.randint(20, size=50),
        'z': np.random.randint(10, size=50)}

options = sorted(data.keys())
key_property_x = Select(title="Option:", value=options[0], options=options)


def update_plot():
    return dict(x=data[key_property_x.value],
                y=data[key_property_x.value])


p = figure(tools='pan,box_zoom,box_select,reset,wheel_zoom, undo,redo,save',
           match_aspect=True, background_fill_color='white', toolbar_location="above")
p.grid.visible = False

source_bin = ColumnDataSource(data=dict(r=[], q=[], counts=[]))
hex_size = 0.05

def normalize_data(data):
    
    #Normalized data to max
    if (data.max(axis=0) != 0):
        data_normed = data / data.max(axis=0)
    else:
        data_normed = data
        
    return data_normed

def data_bin_change(attr, old, new):
    p.xaxis.axis_label = p.yaxis.axis_label = new
    
    
    p.extra_y_ranges = {"yraw": Range1d(start=min(data[new]),
                                end=max(data[new]),
                                )}
    p.add_layout(LinearAxis(y_range_name="yraw"), 'right') 
    
    
    x_normed = normalize_data(data[new])
    y_normed = normalize_data(data[new])

    
    bins = hexbin(x_normed, y_normed, hex_size)

    source_bin.data = dict(r=bins.r, q=bins.q, counts=bins.counts)


key_property_x.on_change("value", data_bin_change)

# Populate the initial data.
data_bin_change(None, None, key_property_x.value)

cmap = log_cmap('counts', 'Cividis256', 1, max(source_bin.data['counts']))

h = p.hex_tile(q="q", r="r", size=hex_size, line_color=None,
               source=source_bin, fill_color=cmap)

p.add_tools(HoverTool(tooltips=[('Counts', '@counts')],
                      mode='mouse',
                      point_policy='follow_mouse',
                      renderers=[h]))

p.add_layout(ColorBar(color_mapper=cmap['transform'],
                      location=(0, 0),
                      ticker=LogTicker()),
             'right')

curdoc().add_root(row(column(key_property_x), p))

Even if your code worked as required, each change would add a new axis each time. Instead, you should just update the range when the selected key property changes:

import numpy as np

from bokeh.io import curdoc
from bokeh.layouts import column, row
from bokeh.models import ColorBar, LogTicker, HoverTool, ColumnDataSource, Select
from bokeh.plotting import figure
from bokeh.transform import log_cmap
from bokeh.util.hex import hexbin
from bokeh.models import Range1d, LinearAxis

data = {'x': np.random.randint(200, size=50),
        'y': np.random.randint(20, size=50),
        'z': np.random.randint(10, size=50)}

options = sorted(data.keys())
key_property_x = Select(title="Option:", value=options[0], options=options)


def update_plot():
    return dict(x=data[key_property_x.value],
                y=data[key_property_x.value])


p = figure(tools='pan,box_zoom,box_select,reset,wheel_zoom, undo,redo,save',
           match_aspect=True, background_fill_color='white', toolbar_location="above")
p.grid.visible = False

source_bin = ColumnDataSource(data=dict(r=[], q=[], counts=[]))
hex_size = 0.05


def normalize_data(data):
    # Normalized data to max
    if (data.max(axis=0) != 0):
        data_normed = data / data.max(axis=0)
    else:
        data_normed = data

    return data_normed


p.extra_y_ranges = {"yraw": Range1d(start=min(data[key_property_x.value]),
                                    end=max(data[key_property_x.value]))}
p.add_layout(LinearAxis(y_range_name="yraw"), 'right')


def data_bin_change(attr, old, new):
    p.xaxis.axis_label = p.yaxis.axis_label = new
    p.extra_y_ranges['yraw'].update(start=min(data[key_property_x.value]),
                                    end=max(data[key_property_x.value]))

    x_normed = normalize_data(data[new])
    y_normed = normalize_data(data[new])

    bins = hexbin(x_normed, y_normed, hex_size)

    source_bin.data = dict(r=bins.r, q=bins.q, counts=bins.counts)


key_property_x.on_change("value", data_bin_change)

# Populate the initial data.
data_bin_change(None, None, key_property_x.value)

cmap = log_cmap('counts', 'Cividis256', 1, max(source_bin.data['counts']))

h = p.hex_tile(q="q", r="r", size=hex_size, line_color=None,
               source=source_bin, fill_color=cmap)

p.add_tools(HoverTool(tooltips=[('Counts', '@counts')],
                      mode='mouse',
                      point_policy='follow_mouse',
                      renderers=[h]))

p.add_layout(ColorBar(color_mapper=cmap['transform'],
                      location=(0, 0),
                      ticker=LogTicker()),
             'right')

curdoc().add_root(row(column(key_property_x), p))

Thank you, solution works. I understand now where I was making error.

Hi! Please see if you can answer this: