Legend issue when updating plot

Hello everyone,
I’ve been stuck on this problems for days and I hope someone can suggest me a way to get out of this.

I need to create a minimal dashboard to show the distribution of data coming from different series, in order to do this I’m using a Select dropdown with a CustomJS callback that updates a figure. To show extra information, different colors are used to distinguish the points according to the values of a column and these values are displayed in a Legend.

Unfortunately, not all the series contain all the possible values for that column and this seems to create trouble to the Legend because, when updating the plot, it always shows all the possible values, but with a random color associated to the values that are missing for a specific series.

I’ll try to show an example hoping to be more clear. On the left you can see a Legend for a series that contains all the values (A, B, C, D) and it’s displayed correctly; on the right the Legend for a series that contains only A, B, C: as you can see it still shows the D value associated to a repeated color
Immagine

I’m leaving a minimal working example to reproduce this issue

MWE
from bokeh.io import output_notebook, show
from bokeh.layouts import row
from bokeh.models import CategoricalColorMapper, ColumnDataSource, CustomJS, Select
from bokeh.palettes import Spectral4
from bokeh.plotting import figure

output_notebook()

data_dict = {
    'alpha': {
        'x': [2, 1, 4, 3],
        'y': [1, 2, 3, 4],
        'c': ['A', 'B', 'C', 'D']
    },
    'beta': {
        'x': [3, 2, 1],
        'y': [1, 2, 3],
        'c': ['A', 'B', 'C']
    }
}

df = pd.DataFrame(data=data_dict['alpha'])
source = ColumnDataSource(data=df)

plot = figure(width=800)

color_map = CategoricalColorMapper(factors=df['c'].unique(), palette=Spectral4)

o = plot.circle(x='x', y='y', source=source, size=6, legend_field='c', color={'field': 'c', 'transform': color_map})

select_series = Select(value='alpha', title='series', options=['alpha', 'beta'])

cb = CustomJS(args=dict(select_series=select_series, source=source, data_dict=data_dict), code="""
    const series = select_series.value
    
    const x = Array.from(data_dict[series]['x'])
    const y = Array.from(data_dict[series]['y'])
    const c = Array.from(data_dict[series]['c'])
    
    source.data = {x, y, c}
    source.change.emit()
""")

select_series.js_on_change('value', cb)

show(row(select_series, plot))

I’m fine with having either a “fixed” Legend that displays (correctly) all the values even if some of them are missing for the current plot or adding/removing items from it according to the data that it’s being currently displayed.

I remain at your disposal in case I haven’t been clear enough.
Thank you in advance!

@mpasson Are you using an old version of Bokeh? Please always state relevant version information with any OSS support request. When I try your code with recent versions, the “D” row is removed entirely from the legend:

Screenshot 2023-06-28 at 10.04.25

So my first suggestion is simply to try things on the with the latest version.

FYI this is not necessary;

    source.change.emit()

The only time to do that is if columns are being updated “in-place” since BokehJS cannot automatically detect those kinds of changes.

@Bryan Sorry about that, I’m using Bokeh 3.2.0 and Python 3.11.3 on Windows 10. Running pip install --upgrade bokeh says I’m at the latest version. I don’t know if it’s relevant, but I’m using Bokeh in a notebook environment and it says BokeJS 3.1.1 successfully loaded when I run output_notebook().

I’m trying to cram all these information in a single screenshot, as you can see when selecting the series “beta” I still have the “D” item in the Legend.

That means you are using Bokeh 3.1.1. not 3.2.0, as you will also be able to verify by inspecting bokeh.__version__. Perhaps you have an environment mix-up and you are running ipython notebook from a different environment than where Bokeh 3.2.0 was installed? But my suggestion still stands, i.e. to try things out with Bokeh 3.2

I had to manually update BokehJS to the latest version with npm i @bokeh/bokehjs but now the problem I mentioned in my first post doesn’t occur anymore which is all that matters.

Thank you for your time!

I’m glad things are working @mpasson however I think you must have inadvertently fixed your env/package issue in some other way. When using Python APIs, BokehJS is loaded either from the bundled version shipped inside the Python package (which npm can’t affect) or from remote cdn.bokeh.org (which npm can’t affect), and in any case the version loaded is tied to the Python version, not anything external to the Python package. The npm BokehJS package is only really ever useful or relevant for folks building their own JavaScript applications directly, without Python.

I wonder if the fact that I wrote and run the MWE inside a jupyter notebook in a web browser is relevant. When I ran the code the first time I had BokehJS 3.1.1 successfully loaded in the first output cell, then I updated Bokeh to 3.2.0 via pip and I still had that message and when I ran npm i @bokeh/bokehjs the output of the first cell changed to BokehJS 3.2.0 successfully loaded without even re-running it.

Anyway, I created a new virtual environment from scratch in another PC, this time setting bokeh==3.2.0 in the requirements, and the (real) command line script worked like a charm without having to fiddle with npm.

1 Like

Notebooks definitely add another level of complexity. Restarting/clearing outputs is usually advised to make sure a newly installed version is reflected inside notebooks.

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