Bokeh pie-chart : dropdown does not interact on Jupyter/Python

I have started bokeh JS callback and it is not able to interact properly with my piechart callback. The new piechart of years is not displayed despite the callback. An identical code has work for barcharts. The angles and colors have been provided as lists in the df.

I have two options in my dropdown list and have assigned a seperate .wedge() to each month and year. Have tried assigning callback in the JS form. The pie_chart_month overrides pie_chart_year and that is the only reason why this is displayed

#data for years(df_year)
#   index   years   counts  angle   color
#0  1   2013    1267562 2.712785    #99d594
#1  2   2014    1055861 2.259711    #ffffbf
#2  3   2015    612426  1.310689    #fc8d59
# data for months(df_months)
# .    index    months  counts  angle           color
#0  1   Jan 4562    2.712785    #99d594
#1  2   Feb 2414    2.259711    #fffbbf
#2  3   Mar 4234    1.310689    #fc8d59
#
#
#------------------
pie_chart = figure(plot_height=350, title="Pie Chart", toolbar_location=None,
           tools="hover", tooltips=[("Counts", "@counts")])
pie_chart_year = pie_chart.wedge(x=0, y=1, radius=0.4, 
                       start_angle=cumsum('angle', include_zero=True), 
                      end_angle=cumsum('angle'),
                       line_color="white", fill_color='color', legend='years', source=df_year)
pie_chart_month = pie_chart.wedge(x=0, y=1, radius=0.4, 
                                    start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'),
                                    line_color="white", fill_color='color', legend='months', source=df_month)

source_pie_month = ColumnDataSource(df_month)
source_pie_year = ColumnDataSource(df_year)
#callback for input controls
pie_chart_callback = CustomJS(args=dict(months=source_pie_month, years=source_pie_month, plot=pie_chart), code="""
    if (ui_view1.value=="months") {
         plot.factors = months.data_source.data.months  

    } else {

        plot.factors = years.data_source.data.years
    }
    plot.change.emit()
""")

ui_view1 = Select(title="View", callback=pie_chart_callback, value="months", options=["months", "years"])

ui_view1.js_on_change('value', pie_chart_callback)
# layout
layout = column(ui_view1, pie_chart)
show(layout)

Expected Result: The piechart month would change to the month when month is selected. Piechart year would change to year if year is selected

@theAkhileshRai Plot objects do not have a factors property, so immediately, I would not expect the CustomJS code that does plot.factors = ... to do anything. When you have a categorical axis (i.e. for a bar chart) then you might set plot.x_range.factors = ... And this could be a simple way to “switch” between two versions of a bar chart possibly, since Bokeh will ignore any coordinates with factors that are not on the range. (You’d just put all the data for every version in one data source and let the configuration of factors on the range “choose” between them).

But that would not work in this case at all. The axes here are continuous, numerical axes, in all cases. They just have a (numeric) start and end, no configurable set of factors. To update the wedges, you will have to update the data source that drives the wedge. There are several examples of updating data sources from CustomJS code in the docs:

https://bokeh.pydata.org/en/latest/docs/user_guide/interaction/callbacks.html

Actually I suppose you could draw both of your pie charts and adjust the numeric range start and end so that only one is visible on screen at at time, based on the Select value. Seems a bit “hacky” to me but would certainly work.

1 Like

@Bryan I understand what I did wrong with the factors property and about the continuous, numerical axes too. But shouldn’t this work without me driving through the wedge. Individually both(month and year) pie charts work. I just want to put that in a dropdown list. Can you show me it?

But shouldn’t this work without me driving through the wedge.

I don’t really understand the question. A glyph displays the data from its data source. It stands to reason that if you want to change what the glyph is displaying, you change its data.

Another idea: You could draw both versions by explicitly using two wedge glyphs, and then control their visibility:

years = plot.wedge(...)
months = plot.wedge(...)

years.glyph.visible = False

You can use the CustomJS to update the .visible properties as appropriate.

Can you show me it?

I’m happy to offer suggestions or feedback on your code attempts. I do not have the bandwidth to create whole working solutions from scratch.

1 Like

for now I have this code and it throws the same piechart and nothing happens with JS callback

pie_chart_callback = CustomJS(args=dict(months=source_pie_month, years=source_pie_month, plot=pie_chart), code="""
if (ui_view1.value=="months") {
    var data1 = months.data
    var angle = data1['angle']
    months = plot.wedge(x=0, y=1, radius=0.4, 
                   start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'),
                   line_color="white", fill_color='color', legend='data1', source=data1)

     
     
} else {


    var data1 = years.data
    var angle = data1['angle']
    years = plot.wedge(x=0, y=1, radius=0.4, 
                   start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'),
                   line_color="white", fill_color='color', legend='data1', source=data1)

     
  
}

ui_view1 = Select(title=“View”, callback=pie_chart_callback, value=“months”, options=[“months”, “years”])

ui_view1.js_on_change(‘value’, pie_chart_callback)

layout

layout = column(ui_view1, pie_chart)
show(layout)
“”")

I am afraid you have some serious misunderstandings. The CustomJS code can only have JavaScript code. The browser does not know anything at all about Python, so putting Python code in a CustomJS as you have done above can never work. I think using the Select to toggle the visibility will be simplest for you. Here is the pseudocode expanded:

# this is python code 
years = plot.wedge(...)
months = plot.wedge(...)

# pick which one is visible first (should match Select default)
years.glyph.visible = True
months.glyph.visible = False 

# pass select, years, months as args, the callback code sets visible values and nothing else
select.on_change('value', args=dict(...), code="""
    if (select.value == "years") {
        # set years.glyph.visible = true, and months.glyph.visible = false
    } else {
        # set months.glyph.visible = true, and years.glyph.visible = false
    }
""")
1 Like

Thanks this works :). Willmake sure to keep your suggestions in mind. :slight_smile:

1 Like