带bokeh服务器的Python回调-选择返回空白图的小部件

问题描述 投票:0回答:1

我最近学习了如何使用bokeh,但在使python回调与bokeh服务器一起工作时遇到了麻烦。

这是我的代码(我正在使用天气数据构建烛台图:]

from bokeh.client import push_session
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, DatetimeTickFormatter, CDSView, BooleanFilter,HoverTool
from bokeh.models import Select
from bokeh.layouts import column, widgetbox
from make_datasets import hourly_all_winters


# Convert dataset to a column data source
source = ColumnDataSource(data={
    'date': hourly_all_winters['date'],
    'max': hourly_all_winters['max'],
    'min': hourly_all_winters['min'],
    'first': hourly_all_winters['first'],
    'last': hourly_all_winters['last'],
    'average': hourly_all_winters['mean'],
    'winter': hourly_all_winters['winter'],
})

# Create first plot and select only the box_zoom and reset tools
y_range = (-30, 30)
p = figure(y_range=y_range, x_axis_type="datetime", plot_width=950, plot_height=200,
            title=f"Daily temperature variations - winter {source.data['winter'][0]}",
            x_axis_label='Months', y_axis_label='Temperature in °C',
            tools="box_zoom,reset")
p.xaxis.formatter = DatetimeTickFormatter(months=['%B'])

# Create the range line glyph
p.segment('date', 'max', 'date', 'min', source=source, color="black")

# Create the ascending bars glyph - we need to create a view of our data with a boolean mask to only plot the data
# we want
booleans_inc = [True if last > first else False for last, first in zip(source.data['last'], source.data['first'])]
view_inc = CDSView(source=source, filters=[BooleanFilter(booleans_inc)])

booleans_dec = [True if last < first else False for last, first in zip(source.data['last'], source.data['first'])]
view_dec = CDSView(source=source, filters=[BooleanFilter(booleans_dec)])

w = 365 * 60 * 2000

p.vbar('date', w, 'first', 'last', source=source, view=view_inc, fill_color="#2c8cd1", line_color="#2c8cd1")
p.vbar('date', w, 'first', 'last', source=source, view=view_dec, fill_color="#F2583E", line_color="#F2583E")

# Create a hover tool so that we can see min, max, first and last values for each record
# over the plot
hover = HoverTool(tooltips=[("First", "@first{first.1f}"),
                            ("Last", "@last{last.1f}"),
                            ("Min", "@min{min.1f}"),
                            ("Max", "@max{max.1f}"), ], mode='vline')

p.add_tools(hover)

# Make a slider object
slider = Select(options=['', '2013-2014', '2014-2015', '2015-2016', '2016-2017', '2017-2018', '2018-2019'],
                value='', title='Winter')

def update_plot(attr, old, new):
    if new == '':
        source.data = ColumnDataSource(data={'date': hourly_all_winters['date'],
                                             'max': hourly_all_winters['max'],
                                             'min': hourly_all_winters['min'],
                                             'first': hourly_all_winters['first'],
                                             'last': hourly_all_winters['last'],
                                             'average': hourly_all_winters['mean'],
                                             'winter': hourly_all_winters['winter'],}).data
    else:
        new_source = ColumnDataSource(data={'date': hourly_all_winters[hourly_all_winters.winter == winter]['date'],
                      'max': hourly_all_winters[hourly_all_winters.winter == new]['max'],
                      'min': hourly_all_winters[hourly_all_winters.winter == new]['min'],
                      'first': hourly_all_winters[hourly_all_winters.winter == new]['first'],
                      'last': hourly_all_winters[hourly_all_winters.winter == new]['last'],
                      'average': hourly_all_winters[hourly_all_winters.winter == new]['mean'],
                      'winter': hourly_all_winters[hourly_all_winters.winter == new]['winter'],
                      })
        source.data = new_source.data

# Attach the callback to the 'value' property of slider
slider.on_change('value', update_plot)

# put the button and plot in a layout and add to the document
curdoc().add_root(column(widgetbox(slider), p))

show(curdoc())

我尝试从“ 2013-2014”数据和完整数据集开始。每次运行bokeh serve myapp.py时,基本可视化效果都很好,但是通过下拉列表选择其他时间段只会返回空白图。对我可能错过的事情有什么想法吗?我还尝试使用push_session()来查看是否有帮助(但我知道无论如何它很快都会被弃用)。

编辑-这是hourly_all_winters数据的示例:

   Year  Month  Day  first  last   min   max       mean       date     winter
0  2013     10    1   17.3  14.5  14.5  23.1  18.529167 2013-10-01  2013-2014
1  2013     10    2   14.8  14.5  13.9  24.0  18.545833 2013-10-02  2013-2014
2  2013     10    3   13.9  14.3  10.0  21.6  15.516667 2013-10-03  2013-2014
3  2013     10    4   14.7  14.3  13.6  16.1  14.900000 2013-10-04  2013-2014
4  2013     10    5   14.0  13.4  12.8  18.2  14.804167 2013-10-05  2013-2014
python python-3.x data-visualization bokeh
1个回答
1
投票

我从bokeh community得到了一个很好的答案-因此在此处发布解决方案:

show(curdoc())不是正确的方法,因此删除最后一行,然后在终端中运行bokeh serve --show myapp.py应该可以解决问题。source.data = new_source.data是我的第二个错误,无法将.data从一个CDS“迁移”到另一个CDS。从Bokeh 2.0开始,这将立即产生异常。 (当前导致静默故障)。 .data的值始终必须是纯Python字典。

下面的更新代码有效:

from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, DatetimeTickFormatter, CDSView, BooleanFilter,HoverTool
from bokeh.models import Select
from bokeh.layouts import column, widgetbox

from make_datasets import hourly_all_winters


# Convert dataset to a column data source
source = ColumnDataSource(data={
    'date': hourly_all_winters['date'],
    'max': hourly_all_winters['max'],
    'min': hourly_all_winters['min'],
    'first': hourly_all_winters['first'],
    'last': hourly_all_winters['last'],
    'average': hourly_all_winters['mean'],
    'winter': hourly_all_winters['winter'],
})

# Create first plot and select only the box_zoom and reset tools
y_range = (-30, 30)
p = figure(y_range=y_range, x_axis_type="datetime", plot_width=950, plot_height=400,
            title=f"Daily temperature variations - winter {source.data['winter'][0]}",
            x_axis_label='Months', y_axis_label='Temperature in °C',
            tools="box_zoom,reset")
p.xaxis.formatter = DatetimeTickFormatter(months=['%B'])

# Create the range line glyph
p.segment('date', 'max', 'date', 'min', source=source, color="black")

# Create the ascending bars glyph - we need to create a view of our data with a boolean mask to only plot the data
# we want
booleans_inc = [True if last > first else False for last, first in zip(source.data['last'], source.data['first'])]
view_inc = CDSView(source=source, filters=[BooleanFilter(booleans_inc)])

booleans_dec = [True if last < first else False for last, first in zip(source.data['last'], source.data['first'])]
view_dec = CDSView(source=source, filters=[BooleanFilter(booleans_dec)])

w = 365 * 60 * 2000

p.vbar('date', w, 'first', 'last', source=source, view=view_inc, fill_color="#2c8cd1", line_color="#2c8cd1")
p.vbar('date', w, 'first', 'last', source=source, view=view_dec, fill_color="#F2583E", line_color="#F2583E")

# Create a hover tool so that we can see min, max, first and last values for each record
# over the plot
hover = HoverTool(tooltips=[("First", "@first{first.1f}"),
                            ("Last", "@last{last.1f}"),
                            ("Min", "@min{min.1f}"),
                            ("Max", "@max{max.1f}"), ], mode='vline')

p.add_tools(hover)

# Make a slider object
slider = Select(options=['', '2013-2014', '2014-2015', '2015-2016', '2016-2017', '2017-2018', '2018-2019'],
                value='', title='Winter')

def update_plot(attr, old, new):
    if new == '':
        source.data = ColumnDataSource(data={'date': hourly_all_winters['date'],
                                             'max': hourly_all_winters['max'],
                                             'min': hourly_all_winters['min'],
                                             'first': hourly_all_winters['first'],
                                             'last': hourly_all_winters['last'],
                                             'average': hourly_all_winters['mean'],
                                             'winter': hourly_all_winters['winter'],}).data
    else:
        new_source = {'date': hourly_all_winters[hourly_all_winters.winter == new]['date'],
                      'max': hourly_all_winters[hourly_all_winters.winter == new]['max'],
                      'min': hourly_all_winters[hourly_all_winters.winter == new]['min'],
                      'first': hourly_all_winters[hourly_all_winters.winter == new]['first'],
                      'last': hourly_all_winters[hourly_all_winters.winter == new]['last'],
                      'average': hourly_all_winters[hourly_all_winters.winter == new]['mean'],
                      'winter': hourly_all_winters[hourly_all_winters.winter == new]['winter'],
                      }
        source.data = new_source

# Attach the callback to the 'value' property of slider
slider.on_change('value', update_plot)
print(slider.value)

# put the button and plot in a layout and add to the document
curdoc().add_root(column(widgetbox(slider), p))
© www.soinside.com 2019 - 2024. All rights reserved.