Patches not updating (but line and circle are) on slider use

I’m going slightly crazy trying to debug this. I am using Patches to highlight area under the curve. The patches are rendered fine on app load (bokeh server 0.12.4).

The plot has a slider. When this is updated, the vertical line and the point on the curve are updated to new locations as expected, but the patches are not. Not only that but I am getting this warning: “Supplying a user-defined data source AND iterable values to glyph methods is deprecated”. This is definitely connected to the patches but cannot work out why.

A few things I’ve double checked (bolded areas in the code below):

  • I am ensuring that plot.patches uses string references ‘x’ and ‘y’ associated with ColumnDataSource **ppf_plot_auc_source (**so I cannot see the reason for the warning above?!).

  • I am ensuring that auc_x and auc_y are “lists of lists” by wrapping the 1d numpy arrays into a list. Printing shape shows that they are always the same shape, even when the slider is updated. Printing also shows that patches coordinates are correctly recalculated on slider update.

Any thoughts or pointers would be greatly appreciated? It feels like I’m missing something really obvious! (code is fully reproducible for those who would be willing to take a couple of minutes to run this)

import numpy as np

import scipy

from scipy import stats

from bokeh.io import curdoc

from bokeh.layouts import gridplot

from bokeh.plotting import figure

from bokeh.models import ResetTool, HoverTool, LassoSelectTool

from bokeh.models import PolySelectTool, BoxSelectTool

from bokeh.models import ColumnDataSource

from bokeh.models.widgets import Slider

from bokeh.models.widgets import Div

OPTIONS

we will create a distribution with these moments

MOMENTS = dict(loc=0, scale=1)

params for creating evenly spaced probabilities between 0.0001 and 0.9999

LO_PROB = 0.01

HI_PROB = 0.99

N_PROBS = 100

lower tail probability

P = 0.025

create a distribution

mydist = scipy.stats.norm(**MOMENTS)

generate probabilities

PROBS = np.linspace(start=LO_PROB, stop=HI_PROB, num=N_PROBS)

calculate quantiles of given list of probabilities

QUANTILES = mydist.ppf(PROBS)

*** INTERACTIONS

slider to control the lower tail probability

slider_lower_tail_prob = Slider(title=‘Lower Tail Probability’,

                            start=0,

                            end=1,

                            step=0.01,

                            value=P)

tools = [

BoxSelectTool(),

HoverTool(),

PolySelectTool(),

LassoSelectTool(),

ResetTool()

]

def gen_ppf_plot_data(mydist, p):

"""

Generate data for PPF plot curve, vertical line, area

under the curve and point on the curve

"""

# quantile associated with lower tail probability

p = np.array([p])

q = mydist.ppf(p)

# print "p: {0}".format(p.shape)

# print "q: {0}".format(q.shape)

# *** DATA FOR AREA UNDER THE CURVE

# patches xs coordinates is made up of 2 concatenated arrays

# first is x axis positions and

# the second is x positions reversed

auc_x_ = np.linspace(start=QUANTILES[0], stop=q, num=N_PROBS)

auc_x = np.append(auc_x_, auc_x_[::-1])

print "auc_x.shape: {0}".format(auc_x.shape)

# print "\n\nauc_x: {0}\n".format(auc_x)

auc_x = [auc_x]

# patches ys coordinates is made up of 2 concatenated arrays,

# first is zeroes (as we want the shading to start at y=0)

# the second is f(<x positions reversed>)

auc_y = np.append(np.zeros(auc_x_.shape[0]),

                  1 - mydist.sf(auc_x_[::-1]))

print "auc_y.shape: {0}".format(auc_y.shape)

# print "auc_y: {0}\n".format(auc_y)

auc_y = [auc_y]

# *** DATA FOR FUNCTION CURVE

f_x = QUANTILES

f_y = PROBS

# *** DATA FOR VERTICAL LINE (from quantile to curve)

l_x = np.array([q, q])

l_y = np.array([[0], p])

# *** POINT ON CURVE (corresponding lower tail probability)

p_x = np.array([q])

p_y = np.array([p])

return {

    'p': p,

    'q': q,

    'auc_x': auc_x,

    'auc_y': auc_y,

    'f_x': f_x,

    'f_y': f_y,

    'l_x': l_x,

    'l_y': l_y,

    'p_x': p_x,

    'p_y': p_y

}

def update_lower_tail_prob(attr, old, new):

p = slider_lower_tail_prob.value

# update data source objects

# PPF plot

ppf_plot_data = gen_ppf_plot_data(mydist, p)

ppf_plot_qp.data = {'x': ppf_plot_data['q'],

                    'y': ppf_plot_data['p']}

ppf_plot_auc_source.data = {'x': ppf_plot_data['auc_x'],

                            'y': ppf_plot_data['auc_y']}

ppf_plot_f_source.data = {'x': ppf_plot_data['f_x'],

                          'y': ppf_plot_data['f_y']}

ppf_plot_l_source.data = {'x': ppf_plot_data['l_x'],

                          'y': ppf_plot_data['l_y']}

ppf_plot_p_source.data = {'x': ppf_plot_data['p_x'],

                          'y': ppf_plot_data['p_y']}

“”"

PPF Plot (Quantile Function)

“”"

create data source objects

ppf_plot_data = gen_ppf_plot_data(mydist, P)

ppf_plot_qp = ColumnDataSource(

data={'x': ppf_plot_data['q'],

      'y': ppf_plot_data['p']})

ppf_plot_auc_source = ColumnDataSource(

data={'x': ppf_plot_data['auc_x'],

      'y': ppf_plot_data['auc_y']})

ppf_plot_f_source = ColumnDataSource(

data={'x': ppf_plot_data['f_x'],

      'y': ppf_plot_data['f_y']})

ppf_plot_l_source = ColumnDataSource(

data={'x': ppf_plot_data['l_x'],

      'y': ppf_plot_data['l_y']})

ppf_plot_p_source = ColumnDataSource(

data={'x': ppf_plot_data['p_x'],

      'y': ppf_plot_data['p_y']})

quantile associated with lower tail probability

q = max(ppf_plot_qp.data[‘x’])

p = max(ppf_plot_qp.data[‘y’])

construct plot elements

plot = figure(plot_width=600,

          plot_height=400,

          title='Quantiles ~ Lower Tail Probabilities (PPF)',

          tools=tools)

*** AREA UNDER THE CURVE

plot.patches(xs=‘x’, ys=‘y’,

fill_color=[‘LightSteelBlue’],

line_color=None,

alpha=0.6,

source=ppf_plot_auc_source)

*** FUNCTION CURVE

plot.line(x=‘x’, y=‘y’,

      line_color='SteelBlue',

      line_width=3,

      source=ppf_plot_f_source)

*** VERTICAL LINE

plot.line(x=‘x’, y=‘y’,

      line_color='black',

      line_width=3,

      source=ppf_plot_l_source)

*** POINT ON CURVE

plot.circle(x=‘x’, y=‘y’,

        line_color='black',

        fill_color='black',

        line_width=3,

        source=ppf_plot_p_source)

text = “”"Quantile corresponding to lower tail probability {p} is {q:.3f} (the quantile below which {pct:.2f}% of values are likely to be lower than)

    """.format(p=p,

               q=q,

               pct=p * 100.0)

div = Div(text=text, width=600, css_classes=[“customdiv”])

slider_lower_tail_prob.on_change(‘value’, update_lower_tail_prob)

grid = gridplot([[slider_lower_tail_prob], [plot], [div]])

curdoc().add_root(grid)

``

Thank you!

Oh this is odd. I seem to have fixed this after lots of trial and error, but I don’t understand the fix.

I have replaced this inside the update(…):

ppf_plot_auc_source.data = {‘xs’: ppf_plot_data[‘auc_x’],

‘ys’: ppf_plot_data[‘auc_y’]}

``

With this

ppf_plot_auc_source.data[‘xs’] = ppf_plot_data[‘auc_x’]

ppf_plot_auc_source.data[‘ys’] = ppf_plot_data[‘auc_y’]

``

This has done the trick. But can anyone explain why the above does not work when from what I’ve been reading it’s recommended to update the .data attribute of a data source all at once?

Thanks.

···

On Wednesday, 5 April 2017 21:37:17 UTC+1, Carmen Mardiros wrote:

I’m going slightly crazy trying to debug this. I am using Patches to highlight area under the curve. The patches are rendered fine on app load (bokeh server 0.12.4).

The plot has a slider. When this is updated, the vertical line and the point on the curve are updated to new locations as expected, but the patches are not. Not only that but I am getting this warning: “Supplying a user-defined data source AND iterable values to glyph methods is deprecated”. This is definitely connected to the patches but cannot work out why.

A few things I’ve double checked (bolded areas in the code below):

  • I am ensuring that plot.patches uses string references ‘x’ and ‘y’ associated with ColumnDataSource **ppf_plot_auc_source (**so I cannot see the reason for the warning above?!).
  • I am ensuring that auc_x and auc_y are “lists of lists” by wrapping the 1d numpy arrays into a list. Printing shape shows that they are always the same shape, even when the slider is updated. Printing also shows that patches coordinates are correctly recalculated on slider update.

Any thoughts or pointers would be greatly appreciated? It feels like I’m missing something really obvious! (code is fully reproducible for those who would be willing to take a couple of minutes to run this)

import numpy as np

import scipy

from scipy import stats

from bokeh.io import curdoc

from bokeh.layouts import gridplot

from bokeh.plotting import figure

from bokeh.models import ResetTool, HoverTool, LassoSelectTool

from bokeh.models import PolySelectTool, BoxSelectTool

from bokeh.models import ColumnDataSource

from bokeh.models.widgets import Slider

from bokeh.models.widgets import Div

OPTIONS

we will create a distribution with these moments

MOMENTS = dict(loc=0, scale=1)

params for creating evenly spaced probabilities between 0.0001 and 0.9999

LO_PROB = 0.01

HI_PROB = 0.99

N_PROBS = 100

lower tail probability

P = 0.025

create a distribution

mydist = scipy.stats.norm(**MOMENTS)

generate probabilities

PROBS = np.linspace(start=LO_PROB, stop=HI_PROB, num=N_PROBS)

calculate quantiles of given list of probabilities

QUANTILES = mydist.ppf(PROBS)

*** INTERACTIONS

slider to control the lower tail probability

slider_lower_tail_prob = Slider(title=‘Lower Tail Probability’,

                            start=0,
                            end=1,
                            step=0.01,
                            value=P)

tools = [

BoxSelectTool(),
HoverTool(),
PolySelectTool(),
LassoSelectTool(),
ResetTool()

]

def gen_ppf_plot_data(mydist, p):

"""
Generate data for PPF plot curve, vertical line, area
under the curve and point on the curve
"""
# quantile associated with lower tail probability
p = np.array([p])
q = mydist.ppf(p)
# print "p: {0}".format(p.shape)
# print "q: {0}".format(q.shape)
# *** DATA FOR AREA UNDER THE CURVE
# patches xs coordinates is made up of 2 concatenated arrays
# first is x axis positions and
# the second is x positions reversed
auc_x_ = np.linspace(start=QUANTILES[0], stop=q, num=N_PROBS)
auc_x = np.append(auc_x_, auc_x_[::-1])
print "auc_x.shape: {0}".format(auc_x.shape)
# print "\n\nauc_x: {0}\n".format(auc_x)

auc_x = [auc_x]

# patches ys coordinates is made up of 2 concatenated arrays,
# first is zeroes (as we want the shading to start at y=0)
# the second is f(<x positions reversed>)
auc_y = np.append(np.zeros(auc_x_.shape[0]),
                  1 - mydist.sf(auc_x_[::-1]))
print "auc_y.shape: {0}".format(auc_y.shape)
# print "auc_y: {0}\n".format(auc_y)

auc_y = [auc_y]

# *** DATA FOR FUNCTION CURVE
f_x = QUANTILES
f_y = PROBS
# *** DATA FOR VERTICAL LINE (from quantile to curve)
l_x = np.array([q, q])
l_y = np.array([[0], p])
# *** POINT ON CURVE (corresponding lower tail probability)
p_x = np.array([q])
p_y = np.array([p])
return {
    'p': p,
    'q': q,
    'auc_x': auc_x,
    'auc_y': auc_y,
    'f_x': f_x,
    'f_y': f_y,
    'l_x': l_x,
    'l_y': l_y,
    'p_x': p_x,
    'p_y': p_y
}

def update_lower_tail_prob(attr, old, new):

p = slider_lower_tail_prob.value
# update data source objects
# PPF plot
ppf_plot_data = gen_ppf_plot_data(mydist, p)
ppf_plot_qp.data = {'x': ppf_plot_data['q'],
                    'y': ppf_plot_data['p']}
ppf_plot_auc_source.data = {'x': ppf_plot_data['auc_x'],
                            'y': ppf_plot_data['auc_y']}
ppf_plot_f_source.data = {'x': ppf_plot_data['f_x'],
                          'y': ppf_plot_data['f_y']}
ppf_plot_l_source.data = {'x': ppf_plot_data['l_x'],
                          'y': ppf_plot_data['l_y']}
ppf_plot_p_source.data = {'x': ppf_plot_data['p_x'],
                          'y': ppf_plot_data['p_y']}

“”"

PPF Plot (Quantile Function)

“”"

create data source objects

ppf_plot_data = gen_ppf_plot_data(mydist, P)

ppf_plot_qp = ColumnDataSource(

data={'x': ppf_plot_data['q'],
      'y': ppf_plot_data['p']})

ppf_plot_auc_source = ColumnDataSource(

data={'x': ppf_plot_data['auc_x'],
      'y': ppf_plot_data['auc_y']})

ppf_plot_f_source = ColumnDataSource(

data={'x': ppf_plot_data['f_x'],
      'y': ppf_plot_data['f_y']})

ppf_plot_l_source = ColumnDataSource(

data={'x': ppf_plot_data['l_x'],
      'y': ppf_plot_data['l_y']})

ppf_plot_p_source = ColumnDataSource(

data={'x': ppf_plot_data['p_x'],
      'y': ppf_plot_data['p_y']})

quantile associated with lower tail probability

q = max(ppf_plot_qp.data[‘x’])

p = max(ppf_plot_qp.data[‘y’])

construct plot elements

plot = figure(plot_width=600,

          plot_height=400,
          title='Quantiles ~ Lower Tail Probabilities (PPF)',
          tools=tools)

*** AREA UNDER THE CURVE

plot.patches(xs=‘x’, ys=‘y’,

fill_color=[‘LightSteelBlue’],

line_color=None,

alpha=0.6,

source=ppf_plot_auc_source)

*** FUNCTION CURVE

plot.line(x=‘x’, y=‘y’,

      line_color='SteelBlue',
      line_width=3,
      source=ppf_plot_f_source)

*** VERTICAL LINE

plot.line(x=‘x’, y=‘y’,

      line_color='black',
      line_width=3,
      source=ppf_plot_l_source)

*** POINT ON CURVE

plot.circle(x=‘x’, y=‘y’,

        line_color='black',
        fill_color='black',
        line_width=3,
        source=ppf_plot_p_source)

text = “”"Quantile corresponding to lower tail probability {p} is {q:.3f} (the quantile below which {pct:.2f}% of values are likely to be lower than)

    """.format(p=p,
               q=q,
               pct=p * 100.0)

div = Div(text=text, width=600, css_classes=[“customdiv”])

slider_lower_tail_prob.on_change(‘value’, update_lower_tail_prob)

grid = gridplot([[slider_lower_tail_prob], [plot], [div]])

curdoc().add_root(grid)

``

Thank you!