Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

deleting line from figure in bokeh

I am new to Bokeh. I made a widget where when I click a checkbox I want to be able to add/delete a line in a bokeh figure. I have 20 such checkboxes and I dont want to replot the whole figure, just to delete 1 line if a checkbox was unchecked.

This is done through a callback, where I have access to the figure object. I would imagine there is a way to do something like this:

F=figure()
F.line('x', 'y', source=source, name='line1')
F.line('x', 'z', source=source, name='line2')

%%in callback
selected_line_name = 'line1' # this would be determined by checkbox
selected_line = F.children[selected_line_name]
delete(selected_line)

However, I am unable to figure out how to 1) access a glyph from its parent object 2) delete a glyph

I tried setting the datasource 'y'=[], but since all column data sources have to be the same size, this removes all the plots...

like image 310
DankMasterDan Avatar asked Sep 29 '17 01:09

DankMasterDan


2 Answers

There are several ways:

# Keep the glyphs in a variable:
line2 = F.line('x', 'z', source=source, name='line2')

# or get the glyph from the Figure:
line2 = F.select_one({'name': 'line2'})

# in callback:
line2.visible = False
like image 123
Alex Avatar answered Nov 07 '22 13:11

Alex


This will work to maintain a shared 'x' data source column if glyphs are assigned as a variable and given a name attribute. The remove function fills the appropriate 'y' columns with nans, and the restore function replaces nans with the original values.

The functions require numpy and bokeh GlyphRenderer imports. I'm not sure that this method is worthwhile given the simple visible on/off option, but I am posting it anyway just in case this helps in some other use case.

Glyphs to remove or restore are referenced by glyph name(s), contained within a list.

src_dict = source.data.copy()

def remove_glyphs(figure, glyph_name_list):
    renderers = figure.select(dict(type=GlyphRenderer))
    for r in renderers:
        if r.name in glyph_name_list:
            col = r.glyph.y
            r.data_source.data[col] = [np.nan] * len(r.data_source.data[col])

def restore_glyphs(figure, src_dict, glyph_name_list):
    renderers = figure.select(dict(type=GlyphRenderer))
    for r in renderers:
        if r.name in glyph_name_list:
            col = r.glyph.y
            r.data_source.data[col] = src_dict[col]

Example:

from bokeh.plotting import figure, show
from bokeh.io import output_notebook
from bokeh.models import Range1d, ColumnDataSource
from bokeh.models.renderers import GlyphRenderer

import numpy as np

output_notebook()

p = figure(plot_width=200, plot_height=150,
           x_range=Range1d(0, 6),
           y_range=Range1d(0, 10),
           toolbar_location=None)

source = ColumnDataSource(data=dict(x=[1, 3, 5],
                                    y1=[1, 1, 2],
                                    y2=[1, 2, 6],
                                    y3=[1, 3, 9]))

src_dict = source.data.copy()

line1 = p.line('x', 'y1',
               source=source,
               color='blue',
               name='g1',
               line_width=3)

line2 = p.line('x', 'y2',
               source=source,
               color='red',
               name='g2',
               line_width=3)

line3 = p.line('x', 'y3',
               source=source,
               color='green',
               name='g3',
               line_width=3)
print(source.data)
show(p)

out:

{'x': [1, 3, 5], 'y1': [1, 1, 2], 'y2': [1, 2, 6], 'y3': [1, 3, 9]}

enter image description here

remove_glyphs(p, ['g1', 'g2'])
print(source.data)
show(p)

out:

{'x': [1, 3, 5], 'y1': [nan, nan, nan], 'y2': [nan, nan, nan], 'y3': [1, 3, 9]}

enter image description here

restore_glyphs(p, src_dict, ['g1', 'g3'])
print(source.data)
show(p)

('g3' was already on the plot, and is not affected)

out:

{'x': [1, 3, 5], 'y1': [1, 1, 2], 'y2': [nan, nan, nan], 'y3': [1, 3, 9]}

enter image description here

restore_glyphs(p, src_dict, ['g2'])
print(source.data)
show(p)

out:

{'x': [1, 3, 5], 'y1': [1, 1, 2], 'y2': [1, 2, 6], 'y3': [1, 3, 9]}

enter image description here

like image 31
b2002 Avatar answered Nov 07 '22 14:11

b2002