Vbar glyph and axis disappear after callback

Hello,

I created a figure with vbar and line glyphs. I applied custom JS callbacks through the select widget, this widget allows the user to choose their preferred data view: monthly or daily. The default data value that support the glyph are monthly. When selecting the daily value on the widget, the figure’s axes and the glyphs adjust appropriately. When I use the widget to select the month value, then the axes range values and glyphs disappear. Can anyone help me find out why?

Notes:

  • I have noticed that the callbacks work as intended if i comment out the vbar glyph code.
  • I also receive this error on the browser console: “TypeError: t is undefined

Versions:

Python: 3.7.1

Bokeh: 1.0.4

Firefox Browser: 65.0.1

Code:

import pandas as pd
from bokeh.plotting import figure, show
from bokeh.models.sources import ColumnDataSource
from bokeh.models import Range1d, FactorRange, CustomJS
from bokeh.models.widgets import Select
from bokeh.models.glyphs import VBar, Line
from bokeh.layouts import column
import math
#data gen
monthly_data = {‘widgets_made’ : [34.0, 30.0, 40.0, 53.0, 49.0, 62.0],
‘defects’ : [2.0,1.0,3.0,3.0,2.0,4.0]}
daily_data = {‘widgets_made’ : [3.0, 6.0, 5.0, 3.0, 7.0, 10.0,3.0, 6.0, 5.0, 3.0, 7.0, 10.0],
‘defects’ : [1.0,1.0,3.0,3.0,2.0,4.0,1.0,1.0,3.0,3.0,2.0,4.0]}
df_monthly =pd.DataFrame(monthly_data, index=[‘2018-01’, ‘2018-02’,‘2018-03’,‘2018-04’,‘2018-05’,‘2018-06’])
df_daily =pd.DataFrame(daily_data, index=[‘2018-01-01’, ‘2018-01-02’,‘2018-01-03’,‘2018-01-04’,‘2018-01-05’,‘2018-01-06’,
‘2018-01-07’,‘2018-01-08’,‘2018-01-09’,‘2018-01-10’,‘2018-01-11’,‘2018-01-12’])
fill_source = ColumnDataSource(df_monthly)
source_monthly = ColumnDataSource(df_monthly)
source_daily = ColumnDataSource(df_daily)
#VISUALIZATION: code to build chart
xdr = FactorRange(factors=fill_source.data[‘index’])
ydr = Range1d(start = 0, end = int(max(fill_source.data[‘widgets_made’] + 1.05)))
acv = figure(plot_width=1310, plot_height=400, x_range=xdr, y_range=ydr,x_axis_label=‘Date Range’, y_axis_label=‘Count’)
acv_bar = acv.vbar(x=‘index’, top=‘widgets_made’, width=.5, legend=‘Completed Tests’,source=fill_source)
acv_line = acv.line(‘index’, ‘defects’, line_width=2, color=‘red’, source=fill_source)
acv.xaxis.major_label_orientation = math.pi / 2
#Custom JS code for callback
codes = “”"
var f = cb_obj.value;
var sdata = source.data;
var data1 = monthly.data;
var data2 = daily.data;

if (f == "monthly") {
for (key in data1) {
    sdata[key] = [];
    for (i=0;i<data1[key].length;i++){
    sdata[key].push(data1[key][i]);
    }
}
    //updating axes
    var y_range_arr=sdata['widgets_made'];
    var yrange_max = Math.max(...y_range_arr) * 1.05;
    yrange.end = yrange_max;
    xrange.factors = sdata['index'];
    console.log(sdata);
} else if (f == "daily") {
for (key in data2) {
    sdata[key] = [];
    for (k=0;k<data2[key].length;k++){
    sdata[key].push(data2[key][k]);
    }
}
    //updating axes
    var y_range_arr=sdata['widgets_made'];
    var yrange_max = Math.max(...y_range_arr) * 1.05;
    yrange.end = yrange_max;
    xrange.factors = sdata['index'];
    console.log(sdata)
};
yrange.change.emit();
xrange.change.emit();
source.change.emit();

“”"
callback = CustomJS(args=dict(source=fill_source, monthly=source_monthly, daily=source_daily,
yrange=acv.y_range, xrange=acv.x_range),code=codes)
#WIDGET: Building drop-down to choose different levels of aggregation
select = Select(title = ‘Data Aggregation Levels:’, value=‘monthly’,
options=[‘monthly’,‘daily’],callback=callback)
show(column(select,acv))

``

Hi,

The reason for this is a because updating factors is a bit tricky. If you update the ranges first, then you have an instant where the old data in your CDS does not map to the new factors. If you update the data first, you have an instance where there new data in your CDS does not map to the old factors. Without getting into the gory details, this is especially problematic when then length of the data changes as your example does.

The solution is to make sure the data change is always emitted before the range changes. The following code works for me:

    codes = '''
        var f = cb_obj.value;
        var sdata = source.data;
        var data1 = monthly.data;
        var data2 = daily.data;
        if (f == "monthly") {
            for (key in data1) {
                sdata[key] = ;
                for (i=0;i<data1[key].length;i++){
                sdata[key].push(data1[key][i]);
                }
            }
            source.change.emit(); // NEW
            var y_range_arr=sdata['widgets_made'];
            var yrange_max = Math.max(...y_range_arr) * 1.05;
            yrange.end = yrange_max;
            xrange.factors = sdata['index'];
        } else if (f == "daily") {
            for (key in data2) {
                sdata[key] = ;
                for (k=0;k<data2[key].length;k++){
                sdata[key].push(data2[key][k]);
                }
            }
            source.change.emit(); // NEW
            var y_range_arr=sdata['widgets_made'];
            var yrange_max = Math.max(...y_range_arr) * 1.05;
            yrange.end = yrange_max;
            xrange.factors = sdata['index'];
        };
        source.change.emit();
    '''

Thanks,

Bryan

···

On Mar 4, 2019, at 4:11 PM, [email protected] wrote:

Hello,

I created a figure with vbar and line glyphs. I applied custom JS callbacks through the select widget, this widget allows the user to choose their preferred data view: monthly or daily. The default data value that support the glyph are monthly. When selecting the daily value on the widget, the figure's axes and the glyphs adjust appropriately. When I use the widget to select the month value, then the axes range values and glyphs disappear. Can anyone help me find out why?

Notes:
  • I have noticed that the callbacks work as intended if i comment out the vbar glyph code.
  • I also receive this error on the browser console: "TypeError: t is undefined"
Versions:
    Python: 3.7.1
    Bokeh: 1.0.4
    Firefox Browser: 65.0.1

Code:
import pandas as pd
from bokeh.plotting import figure, show
from bokeh.models.sources import ColumnDataSource
from bokeh.models import Range1d, FactorRange, CustomJS
from bokeh.models.widgets import Select
from bokeh.models.glyphs import VBar, Line
from bokeh.layouts import column
import math
#data gen
monthly_data = {'widgets_made' : [34.0, 30.0, 40.0, 53.0, 49.0, 62.0],
                'defects' : [2.0,1.0,3.0,3.0,2.0,4.0]}
daily_data = {'widgets_made' : [3.0, 6.0, 5.0, 3.0, 7.0, 10.0,3.0, 6.0, 5.0, 3.0, 7.0, 10.0],
              'defects' : [1.0,1.0,3.0,3.0,2.0,4.0,1.0,1.0,3.0,3.0,2.0,4.0]}
df_monthly =pd.DataFrame(monthly_data, index=['2018-01', '2018-02','2018-03','2018-04','2018-05','2018-06'])
df_daily =pd.DataFrame(daily_data, index=['2018-01-01', '2018-01-02','2018-01-03','2018-01-04','2018-01-05','2018-01-06',
                                          '2018-01-07','2018-01-08','2018-01-09','2018-01-10','2018-01-11','2018-01-12'])
fill_source = ColumnDataSource(df_monthly)
source_monthly = ColumnDataSource(df_monthly)
source_daily = ColumnDataSource(df_daily)
#VISUALIZATION: code to build chart
xdr = FactorRange(factors=fill_source.data['index'])
ydr = Range1d(start = 0, end = int(max(fill_source.data['widgets_made'] + 1.05)))
acv = figure(plot_width=1310, plot_height=400, x_range=xdr, y_range=ydr,x_axis_label='Date Range', y_axis_label='Count')
acv_bar = acv.vbar(x='index', top='widgets_made', width=.5, legend='Completed Tests',source=fill_source)
acv_line = acv.line('index', 'defects', line_width=2, color='red', source=fill_source)
acv.xaxis.major_label_orientation = math.pi / 2
#Custom JS code for callback
codes = """
    var f = cb_obj.value;
    var sdata = source.data;
    var data1 = monthly.data;
    var data2 = daily.data;

    if (f == "monthly") {
    for (key in data1) {
        sdata[key] = ;
        for (i=0;i<data1[key].length;i++){
        sdata[key].push(data1[key][i]);
        }
    }
        //updating axes
        var y_range_arr=sdata['widgets_made'];
        var yrange_max = Math.max(...y_range_arr) * 1.05;
        yrange.end = yrange_max;
        xrange.factors = sdata['index'];
        console.log(sdata);
    } else if (f == "daily") {
    for (key in data2) {
        sdata[key] = ;
        for (k=0;k<data2[key].length;k++){
        sdata[key].push(data2[key][k]);
        }
    }
        //updating axes
        var y_range_arr=sdata['widgets_made'];
        var yrange_max = Math.max(...y_range_arr) * 1.05;
        yrange.end = yrange_max;
        xrange.factors = sdata['index'];
        console.log(sdata)
    };
    yrange.change.emit();
    xrange.change.emit();
    source.change.emit();

"""
callback = CustomJS(args=dict(source=fill_source, monthly=source_monthly, daily=source_daily,
                    yrange=acv.y_range, xrange=acv.x_range),code=codes)
#WIDGET: Building drop-down to choose different levels of aggregation
select = Select(title = 'Data Aggregation Levels:', value='monthly',
                options=['monthly','daily'],callback=callback)
show(column(select,acv))

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/782cb768-5da3-481a-8bf2-62063c60fe46%40continuum.io\.
For more options, visit https://groups.google.com/a/continuum.io/d/optout\.

Bryan,

Thank you for your help and for the quick response. Your solution worked perfectly. I will keep this in mind going forward.

Also, saw your note from last week, best of luck on your next role! You have been extremely helpful on my Bokeh journey.

-Albert M.

···

On Tuesday, March 5, 2019 at 10:46:02 AM UTC-6, Bryan Van de ven wrote:

Hi,

The reason for this is a because updating factors is a bit tricky. If you update the ranges first, then you have an instant where the old data in your CDS does not map to the new factors. If you update the data first, you have an instance where there new data in your CDS does not map to the old factors. Without getting into the gory details, this is especially problematic when then length of the data changes as your example does.

The solution is to make sure the data change is always emitted before the range changes. The following code works for me:

codes = '''

    var f = cb_obj.value;

    var sdata = source.data;

    var data1 = monthly.data;

    var data2 = daily.data;

    if (f == "monthly") {

        for (key in data1) {

            sdata[key] = [];

            for (i=0;i<data1[key].length;i++){

            sdata[key].push(data1[key][i]);

            }

        }

        source.change.emit();  // NEW

        var y_range_arr=sdata['widgets_made'];

        var yrange_max = Math.max(...y_range_arr) * 1.05;

        yrange.end = yrange_max;

        xrange.factors = sdata['index'];

    } else if (f == "daily") {

        for (key in data2) {

            sdata[key] = [];

            for (k=0;k<data2[key].length;k++){

            sdata[key].push(data2[key][k]);

            }

        }

        source.change.emit(); // NEW

        var y_range_arr=sdata['widgets_made'];

        var yrange_max = Math.max(...y_range_arr) * 1.05;

        yrange.end = yrange_max;

        xrange.factors = sdata['index'];

    };

    source.change.emit();

'''

Thanks,

Bryan

On Mar 4, 2019, at 4:11 PM, [email protected] wrote:

Hello,

I created a figure with vbar and line glyphs. I applied custom JS callbacks through the select widget, this widget allows the user to choose their preferred data view: monthly or daily. The default data value that support the glyph are monthly. When selecting the daily value on the widget, the figure’s axes and the glyphs adjust appropriately. When I use the widget to select the month value, then the axes range values and glyphs disappear. Can anyone help me find out why?

Notes:
• I have noticed that the callbacks work as intended if i comment out the vbar glyph code.
• I also receive this error on the browser console: “TypeError: t is undefined”

Versions:

Python: 3.7.1
Bokeh: 1.0.4
Firefox Browser: 65.0.1

Code:

import pandas as pd

from bokeh.plotting import figure, show

from bokeh.models.sources import ColumnDataSource

from bokeh.models import Range1d, FactorRange, CustomJS

from bokeh.models.widgets import Select

from bokeh.models.glyphs import VBar, Line

from bokeh.layouts import column

import math

#data gen

monthly_data = {‘widgets_made’ : [34.0, 30.0, 40.0, 53.0, 49.0, 62.0],

            'defects' : [2.0,1.0,3.0,3.0,2.0,4.0]}

daily_data = {‘widgets_made’ : [3.0, 6.0, 5.0, 3.0, 7.0, 10.0,3.0, 6.0, 5.0, 3.0, 7.0, 10.0],

          'defects' : [1.0,1.0,3.0,3.0,2.0,4.0,1.0,1.0,3.0,3.0,2.0,4.0]}

df_monthly =pd.DataFrame(monthly_data, index=[‘2018-01’, ‘2018-02’,‘2018-03’,‘2018-04’,‘2018-05’,‘2018-06’])

df_daily =pd.DataFrame(daily_data, index=[‘2018-01-01’, ‘2018-01-02’,‘2018-01-03’,‘2018-01-04’,‘2018-01-05’,‘2018-01-06’,

                                      '2018-01-07','2018-01-08','2018-01-09','2018-01-10','2018-01-11','2018-01-12'])

fill_source = ColumnDataSource(df_monthly)

source_monthly = ColumnDataSource(df_monthly)

source_daily = ColumnDataSource(df_daily)

#VISUALIZATION: code to build chart

xdr = FactorRange(factors=fill_source.data[‘index’])

ydr = Range1d(start = 0, end = int(max(fill_source.data[‘widgets_made’] + 1.05)))

acv = figure(plot_width=1310, plot_height=400, x_range=xdr, y_range=ydr,x_axis_label=‘Date Range’, y_axis_label=‘Count’)

acv_bar = acv.vbar(x=‘index’, top=‘widgets_made’, width=.5, legend=‘Completed Tests’,source=fill_source)

acv_line = acv.line(‘index’, ‘defects’, line_width=2, color=‘red’, source=fill_source)

acv.xaxis.major_label_orientation = math.pi / 2

#Custom JS code for callback
codes = “”"

var f = cb_obj.value;
var sdata = source.data;
var data1 = monthly.data;
var data2 = daily.data;
if (f == "monthly") {
for (key in data1) {
    sdata[key] = [];
    for (i=0;i<data1[key].length;i++){
    sdata[key].push(data1[key][i]);
    }
}
    //updating axes
    var y_range_arr=sdata['widgets_made'];
    var yrange_max = Math.max(...y_range_arr) * 1.05;
    yrange.end = yrange_max;
    xrange.factors = sdata['index'];
    console.log(sdata);
} else if (f == "daily") {
for (key in data2) {
    sdata[key] = [];
    for (k=0;k<data2[key].length;k++){
    sdata[key].push(data2[key][k]);
    }
}
    //updating axes
    var y_range_arr=sdata['widgets_made'];
    var yrange_max = Math.max(...y_range_arr) * 1.05;
    yrange.end = yrange_max;
    xrange.factors = sdata['index'];
    console.log(sdata)
};
yrange.change.emit();
xrange.change.emit();
source.change.emit();

“”"

callback = CustomJS(args=dict(source=fill_source, monthly=source_monthly, daily=source_daily,

                yrange=acv.y_range, xrange=acv.x_range),code=codes)

#WIDGET: Building drop-down to choose different levels of aggregation

select = Select(title = ‘Data Aggregation Levels:’, value=‘monthly’,

            options=['monthly','daily'],callback=callback)

show(column(select,acv))


You received this message because you are subscribed to the Google Groups “Bokeh Discussion - Public” group.

To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].

To post to this group, send email to [email protected].

To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/782cb768-5da3-481a-8bf2-62063c60fe46%40continuum.io.

For more options, visit https://groups.google.com/a/continuum.io/d/optout.