Search code examples
pythonvisualizationbokehdashboard

Bokeh plot is missing in layout when checkbox group is used


Bokeh scatter plot disappears when checkbox is added to layout.

If I exclude layout = row([checkbox_group, p]) and do show(p), I get the intended scatter plot with the color bar.

But when I include layout = row([checkbox_group, p]) and do show(layout), the scatter plot disappears whereas the checkbox and color bar appear.

import pandas as pd
import numpy as np
from bokeh.plotting import figure, show, curdoc
from bokeh.models import ColumnDataSource, ColorBar, HoverTool, CustomJS
from bokeh.models.widgets import CheckboxGroup
from bokeh.transform import linear_cmap
from bokeh.palettes import Iridescent18
from bokeh.models.mappers import LinearColorMapper
from bokeh.layouts import row

gene_list = ['A', 'B', 'C']
file_paths = [r"home/File1.feather", r"home/File2.feather"]
checkbox_group = CheckboxGroup(labels=['File 1', 'File 2'], active = [0])

def checkbox_change(attr, old, new):
    if new:
        selected_file_index = new[0] 
        if selected_file_index >= len(file_paths):
            print("Selected file index is out of range.")
            return
        selected_file_path = file_paths[selected_file_index]
        df = pd.read_feather(selected_file_path, columns=gene_list + ['umap1', 'umap2', 'index'])
        df = df.replace({'index' : 'cell_type'})

        median_score = []
        for idx, r in df.iterrows():
            score = np.sum(r[gene_list])
            median_score.append(score)
        df['score'] = median_score

        source.data = df.to_dict(orient='list')

    checkbox_group.on_change('active', checkbox_change)

# Create the initial plot
df = pd.read_feather(file_paths[0], columns=gene_list + ['umap1', 'umap2', 'index'])

median_score = []
for idx, r in df.iterrows():
    score = np.sum(r[gene_list])
    median_score.append(score)
df['score'] = median_score

source = ColumnDataSource(df)

mapper = linear_cmap(
    field_name='score', palette=Iridescent18, low=df['score'].min(), high=df['score'].max()
)

p = figure(
    title='UMAP Visualization',
    x_axis_label='umap1',
    y_axis_label='umap2',
    sizing_mode='stretch_width',
    height=1500,
    toolbar_location='above'
)
hover = HoverTool(tooltips=[('Cell Name', '@index')], mode='mouse')
p.add_tools(hover)

p.scatter("umap1", "umap2", color=mapper, source=source)

color_mapper = LinearColorMapper(
    palette=Iridescent18, low=df['score'].min(), high=df['score'].max()
)
color_bar = ColorBar(
    color_mapper=color_mapper, label_standoff=12, location=(0, 0), title='Score'
)
p.add_layout(color_bar, 'right')

layout = row([checkbox_group, p])

show(layout) #show(p)

Solution

  • Your problem comes from the selected sizing_modes in the figure and the row function calls.

    Your are setting the sizing_mode of the figure to stretch_width and you are using the default sizing_mode of the row-layout which is fixed. This leads to a behavoir where the figure is shrunk to a minimal width of 0.

    To fix this, you can

    1. set the sizing_mode of the row to stretch_width or
    2. set the figure to a fixed width

    Minimal Example

    source = ColumnDataSource(dict(A=[1,2,3], B=[1,2,3], score=[2,4,6]))
    
    p = figure(sizing_mode='stretch_width')
    mapper = linear_cmap(
        field_name='score', palette=Iridescent18, low=df['score'].min(), high=df['score'].max()
    )
    p.scatter("A", "B", color=mapper, source=source)
    checkbox_group = CheckboxGroup(labels=['Checkbox 1', 'Checkbox 2'], active = [0])
    color_mapper = LinearColorMapper(palette=Iridescent18, low=1, high=3)
    color_bar = ColorBar(
        color_mapper=color_mapper, label_standoff=12, location=(0, 0), title='Score'
    )
    p.add_layout(color_bar, 'right')
    layout = row([checkbox_group, p], sizing_mode='stretch_width')
    show(layout)
    

    Output

    row with sizing mode stretch width

    Comment

    The output was created with bokeh 3.1.1. I am not sure if this will work for older versions.