Python bokeh slider not refreshing plot

杀马特。学长 韩版系。学妹 提交于 2020-05-18 01:47:05

问题


I am creating a bokeh plot with a slider to refresh plot accordingly. There are 2 issues with the code posted. 1. The plot is not refreshed as per the slider. Please help in providing a fix for this issue. 2. Plot is not displayed with curdoc() when bokeh serve --show fn.ipynb is used

I'm trying to visualise this CSV file.

import pandas as pd
import numpy as np
from bokeh.models import ColumnDataSource, CategoricalColorMapper, HoverTool, Slider
from bokeh.plotting import figure, curdoc
from bokeh.palettes import viridis
from bokeh.layouts import row, widgetbox

#Importing and processing data file 
crop = pd.read_csv('crop_production.csv') 

#Cleaning Data 
crop.fillna(np.NaN) 
crop['Season'] = crop.Season.str.strip() 

#Removing Whitespace #Filtering the dataset by Season 
crop_season = crop[crop.Season == 'Whole Year'] 
crop_dt = crop_season.groupby(['State_Name', 'District_Name', 'Crop_Year']).mean().round(1)

#Creating Column Data Source
source = ColumnDataSource({
        'x'        : crop_dt[crop_dt.index.get_level_values('Year')==2001].loc[(['ABC']), :].Area,
        'y'        : crop_dt[crop_dt.index.get_level_values('Year')==2001].loc[(['ABC']), :].Production,
        'state'    : crop_dt[crop_dt.index.get_level_values('Year')==2001].loc[(['ABC']), :].index.get_level_values('State_Name'),
        'district' : crop_dt[crop_dt.index.get_level_values('Year')==2001].loc[(['ABC']), :].index.get_level_values('District_Name')
})


#Creating color palette for plot
district_list = crop_dt.loc[(['Tamil Nadu']), :].index.get_level_values('District_Name').unique().tolist()
call_colors = viridis(len(district_list))
color_mapper = CategoricalColorMapper(factors=district_list, palette=call_colors)


# Creating the figure
#xmin, xmax = min(data.Crop_Year), max(data.Crop_Year)
#ymin, ymax = min(data.Production), max(data.Production)
p = figure(
    title = 'Crop Area vs Production',
    x_axis_label = 'Area',
    y_axis_label = 'Production',
    plot_height=900, 
    plot_width=1200,
    tools = [HoverTool(tooltips='@district')]
          )
p.circle(x='x', y='y', source=source, size=12, alpha=0.7, 
         color=dict(field='district', transform=color_mapper),
         legend='district')
p.legend.location = 'top_right'


def update_plot(attr, old, new):
    yr = slider.value
    new_data = {
        'x'        : crop_dt[crop_dt.index.get_level_values('Year')==yr].loc[(['ABC']), :].Area,
        'y'        : crop_dt[crop_dt.index.get_level_values('Year')==yr].loc[(['ABC']), :].Production,
        'state'    : crop_dt[crop_dt.index.get_level_values('Year')==yr].loc[(['ABC']), :].index.get_level_values('State_Name'),
        'district' : crop_dt[crop_dt.index.get_level_values('Year')==yr].loc[(['ABC']), :].index.get_level_values('District_Name')
    }
    source.data = new_data

#Creating Slider for Year
start_yr = min(crop_dt.index.get_level_values('Crop_Year'))
end_yr = max(crop_dt.index.get_level_values('Crop_Year'))
slider = Slider(start=start_yr, end=end_yr, step=1, value=start_yr, title='Year')
slider.on_change('value',update_plot)

layout = row(widgetbox(slider), p)
curdoc().add_root(layout)
show(layout)

Also tried a different option using CustomJS as below, but still no luck.

callback = CustomJS(args=dict(source=source), code="""
    var data = source.data;
    var yr = slider.value;
    var x = data['x']
    var y = data['y']
    'x'        = crop_dt[crop_dt.index.get_level_values('Crop_Year')==yr].loc[(['ABC']), :].Area;
    'y'        = crop_dt[crop_dt.index.get_level_values('Crop_Year')==yr].loc[(['ABC']), :].Production;
    p.circle(x='x', y='y', source=source, size=12, alpha=0.7, 
         color=dict(field='district', transform=color_mapper),
         legend='district');
    }
    source.change.emit();
""")



#Creating Slider for Year
start_yr = min(crop_dt.index.get_level_values('Crop_Year'))
end_yr = max(crop_dt.index.get_level_values('Crop_Year'))
yr_slider = Slider(start=start_yr, end=end_yr, step=1, value=start_yr, title='Year', callback=callback)
callback.args["slider"] = yr_slider

回答1:


Had a lot of issues trying to execute your code and I have changed some things, so feel free to correct me if did something wrong.

The error was caused by the creation of the ColumnDataSource, I had to change the level value to Crop_Year instead of Year. The loc 'ABC' also caused an error so I removed that too (And I had to add source = ColumnDataSource({, you probably forgot to copy that)

I also added a dropdown menu so it's possible to only show the data from one district.

Also, I'm not quite sure if it's possible to start a bokeh server by supplying a .ipynb file to --serve. But don't pin me down on that, I never use notebooks. I've tested this with a .py file.

#!/usr/bin/python3
import pandas as pd
import numpy as np
from bokeh.models import ColumnDataSource, CategoricalColorMapper, HoverTool
from bokeh.plotting import figure, curdoc
from bokeh.palettes import viridis
from bokeh.layouts import row, widgetbox
from bokeh.models.widgets import Select, Slider

#Importing and processing data file 
crop = pd.read_csv('crop_production.csv') 

#Cleaning Data 
crop.fillna(np.NaN) 
crop['Season'] = crop.Season.str.strip() 

#Removing Whitespace #Filtering the dataset by Season 
crop_season = crop[crop.Season == 'Whole Year'] 
crop_dt = crop_season.groupby(['State_Name', 'District_Name', 'Crop_Year']).mean().round(1)

crop_dt_year = crop_dt[crop_dt.index.get_level_values('Crop_Year')==2001]
crop_dt_year_state = crop_dt_year[crop_dt_year.index.get_level_values('State_Name')=='Tamil Nadu']

#Creating Column Data Source
source = ColumnDataSource({
    'x': crop_dt_year_state.Area.tolist(), 
    'y': crop_dt_year_state.Production.tolist(), 
    'state': crop_dt_year_state.index.get_level_values('State_Name').tolist(), 
    'district': crop_dt_year_state.index.get_level_values('District_Name').tolist()
})

#Creating color palette for plot
district_list = crop_dt.loc[(['Tamil Nadu']), :].index.get_level_values('District_Name').unique().tolist()
call_colors = viridis(len(district_list))
color_mapper = CategoricalColorMapper(factors=district_list, palette=call_colors)

# Creating the figure
p = figure(
    title = 'Crop Area vs Production',
    x_axis_label = 'Area',
    y_axis_label = 'Production',
    plot_height=900, 
    plot_width=1200,
    tools = [HoverTool(tooltips='@district')]
          )
glyphs = p.circle(x='x', y='y', source=source, size=12, alpha=0.7, 
         color=dict(field='district', transform=color_mapper),
         legend='district')
p.legend.location = 'top_right'

def update_plot(attr, old, new):
    #Update glyph locations
    yr = slider.value
    state  = select.value
    crop_dt_year = crop_dt[crop_dt.index.get_level_values('Crop_Year')==yr]
    crop_dt_year_state = crop_dt_year[crop_dt_year.index.get_level_values('State_Name')==state]
    new_data = {
        'x': crop_dt_year_state.Area.tolist(), 
        'y': crop_dt_year_state.Production.tolist(), 
        'state': crop_dt_year_state.index.get_level_values('State_Name').tolist(), 
        'district': crop_dt_year_state.index.get_level_values('District_Name').tolist()
    }
    source.data = new_data
    #Update colors
    district_list = crop_dt.loc[([state]), :].index.get_level_values('District_Name').unique().tolist()
    call_colors = viridis(len(district_list))
    color_mapper = CategoricalColorMapper(factors=district_list, palette=call_colors)
    glyphs.glyph.fill_color = dict(field='district', transform=color_mapper)
    glyphs.glyph.line_color = dict(field='district', transform=color_mapper)

#Creating Slider for Year
start_yr = min(crop_dt.index.get_level_values('Crop_Year'))
end_yr = max(crop_dt.index.get_level_values('Crop_Year'))
slider = Slider(start=start_yr, end=end_yr, step=1, value=start_yr, title='Year')
slider.on_change('value',update_plot)

#Creating drop down for state
options = list(set(crop_dt.index.get_level_values('State_Name').tolist()))
options.sort()
select = Select(title="State:", value="Tamil Nadu", options=options)
select.on_change('value', update_plot)

layout = row(widgetbox(slider, select), p)
curdoc().add_root(layout)



回答2:


@Jasper Thanks a lot. This works, however it doesnt work with .loc[(['Tamil Nadu']), :]. Reason for having this is to filter the data by adding a bokeh dropdown or radio button object and refresh the plot based on the filters. The below code works only if .loc[(['Tamil Nadu']), :] is removed. Is there any other way to fix this please?

def update_plot(attr, old, new):
    yr = slider.value
    new_data = {
        'x'        : crop_dt[crop_dt.index.get_level_values('Crop_Year')==yr].loc[(['Tamil Nadu']), :].Area.tolist(),
        'y'        : crop_dt[crop_dt.index.get_level_values('Crop_Year')==yr].loc[(['Tamil Nadu']), :].Production.tolist(),
        'state'    : crop_dt[crop_dt.index.get_level_values('Crop_Year')==yr].loc[(['Tamil Nadu']), :].index.get_level_values('State_Name').tolist(),
        'district' : crop_dt[crop_dt.index.get_level_values('Crop_Year')==yr].loc[(['Tamil Nadu']), :].index.get_level_values('District_Name').tolist()
    }
    source.data = new_data


来源:https://stackoverflow.com/questions/54628954/python-bokeh-slider-not-refreshing-plot

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!