使用全局ColumnDataSource时替换布局中的图形和表格

时间:2017-10-11 21:29:43

标签: bokeh

我正在使用散景0.12.9。我有一个表和一个数字,我在回调的全局布局中替换。我通常在构建新的数字/表之前构建ColumnDataSource。现在我想尝试看看我是否可以拥有全局ColumnDataSource,以便我可以通过CDSView调整数据(不需要替换表/数字)。

不幸的是,即使为表和绘图保留单独的CDS和视图也会失败。单击单选按钮几次我收到以下javascript错误: Uncaught TypeError: Cannot read property 'data' of undefined

from datetime import date
from random import randint

from bokeh.models import Line
import numpy as np
import pandas as pd

from bokeh.plotting import figure, output_file, show
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import DataTable, DateFormatter, TableColumn
import bokeh.layouts as layouts
import bokeh.models.widgets as widgets
from bokeh.io import curdoc
from bokeh.models import CustomJS, Slider
from bokeh import palettes
from bokeh.layouts import layout
from bokeh.models import ColumnDataSource, CDSView, IndexFilter
from bokeh.models import widgets


def gen_plot(source=None, view=None):
    p = figure(title='test',
               x_axis_type="datetime",
               plot_width=600, plot_height=400)
    colors = palettes.Category10[10]
    cols = [str(col) for col in source.column_names]
    for ix, col in enumerate(cols):
        if col == 'index':
            continue
        r = p.line(x='index', y=col, source=source, view=view,
                   legend='_' + col,
                   color=colors[ix])
    p.legend.location = "bottom_left"
    return p


def gen_table(source=None, view=None):
    columns = [TableColumn(field=ele, title=ele) for ele
               in source.column_names]
    tab = widgets.DataTable(source=source, view=view, columns=columns,
                            selectable=False,
                            reorderable=False,
                            width=600, height=400)
    return tab



def update(attr, old, new):
    p = gen_plot(source=cdss[0], view=vs[0])
    t = gen_table(source=cdss[1], view=vs[1])

    print l.children
    l.children[1] = p
    l.children[2].children[0] = t


# set up data
cols = ['col1', 'col2', 'col3', 'col4']
df1 = pd.DataFrame(pd.util.testing.getTimeSeriesData())
df1.columns = cols
df2 = pd.DataFrame(pd.util.testing.getTimeSeriesData())
df2.columns = cols
dfs = [df1, df2]
cds1 = ColumnDataSource(df1)
cds2 = ColumnDataSource(df2)
cdss = [cds1, cds2]
filters = [IndexFilter([0, 1, 2, 4])]
filters = []
v1 = CDSView(source=cds1, filters=filters)
v2 = CDSView(source=cds2, filters=filters)
vs = [v1, v2]


# initialize items to replace
p = gen_plot(source=cdss[0], view=vs[0])
t = gen_table(source=cdss[1], view=vs[1])

# initialize controls
radio_wghting = widgets.RadioButtonGroup(labels=["Equal", "Exponential"],
                                         active=0)
radio_wghting.on_change('active', update)

# set up layout
sizing_mode = 'fixed'
l = layout([radio_wghting, p, t], sizing_mode=sizing_mode)

curdoc().add_root(l)
curdoc().title = 'blub'


# call callback initially
update('value', 0, 0)

非常感谢任何提示!

1 个答案:

答案 0 :(得分:0)

  

现在我想尝试看看我是否可以拥有全局ColumnDataSource   我可以通过CDSView调整数据(无需更换   表/图然后)。

您要显示的代码是您尝试替换图形和表格的代码。

以这种方式替换布局对象的子级时,实际上并没有从curdoc中删除以前的图形,并且文档中的其他元素仍然在其引用中包含旧的图形和表格。

你可以尝试这样的东西来直接更新资源。

for rend in p.renderers:
    try:
        rend.data_source
    except AttributeError:
        pass
    else:
        rend.data_source.data.update(new_data_dictionary)

t.source.data.update(new_data_dictionary)

编辑以回复评论

from bokeh.io import curdoc
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, Button
from bokeh.layouts import gridplot, widgetbox

from random import random, choice

import numpy as np

my_data = {1:{'x':[],'y':[],'colo':[],'size':[]}}

kelly_colors = [    '#F3C300','#875692', '#F38400', '#A1CAF1','#BE0032', '#C2B280', '#848482','#008856', '#E68FAC', '#0067A5',
                    '#F99379', '#604E97', '#F6A600','#B3446C', '#DCD300', '#882D17','#8DB600', '#654522', '#E25822','#2B3D26',      ]

x = np.arange(0,50,0.1)

def rand_dict():

    rand_x = [choice(x) for i in range(7)]

    return {'x':rand_x,'y':np.array([random()*100 for i in rand_x]),'colo':np.array([choice(kelly_colors) for i in rand_x]),'size':np.array([(5+int(random()*50)) for i in rand_x])}

def add_stuff():

    global my_data

    my_data[max(my_data.keys())+1] = rand_dict()

    make_doc()

def change_stuff():

    global my_data

    myfig = curdoc().select_one({"name":"myfig"})

    for i,rend in enumerate(myfig.renderers):
        try:
            rend.data_source
        except AttributeError:
            pass
        else:
            my_data[i+1] = rand_dict()
            rend.data_source.data.update(my_data[i+1])

def clear_stuff():

    global my_data

    my_data = {1:{'x':[],'y':[],'colo':[],'size':[]}}

    make_doc()

def make_doc():

    curdoc().clear()

    myfig = figure(plot_width=1000,plot_height=800,outline_line_alpha=0,name='myfig')
    myfig.x_range.start = -5
    myfig.x_range.end = 55
    myfig.y_range.start = -10
    myfig.y_range.end = 110

    myfig.renderers = []

    add_button = Button(label='add stuff',width=100)
    change_button = Button(label='change stuff',width=100)
    clear_button = Button(label='clear stuff',width=100)

    add_button.on_click(add_stuff)
    change_button.on_click(change_stuff)
    clear_button.on_click(clear_stuff)

    grid = gridplot([[myfig,widgetbox(add_button,change_button,clear_button)]],toolbar_location=None)

    curdoc().add_root(grid)

    update_doc()

def update_doc():

    myfig = curdoc().select_one({"name":"myfig"})

    for key in my_data:
        myfig.scatter(x='x',y='y',color='colo',size='size',source=ColumnDataSource(data=my_data[key]))

curdoc().title = 'mytitle'

make_doc()

我喜欢这样做是因为你可以用numpy保存my_data字典,稍后加载它并继续从那里改变你的情节。

def load_data():

    global my_data

    my_data = np.load(path_to_saved_data).item()

    make_doc()

你可以使用pandas数据帧做类似的事情,我对普通词典更加满意。