使用全局ColumnDataSource时替换布局中的图形和表格 [英] Replacing figure and table in layout when using global ColumnDataSource

查看:55
本文介绍了使用全局ColumnDataSource时替换布局中的图形和表格的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用bokeh 0.12.9.我有一个表和一个图,在回调的全局布局中将其替换.我通常在建立新的图形/表格之前就建立 ColumnDataSource .现在,我想尝试看看是否可以有一个全局 ColumnDataSource ,以便可以通过 CDSView 调整数据(然后无需替换表/数字).

I am using bokeh 0.12.9. I have a table and a figure which I replace in the global layout on callback. I usually build the ColumnDataSource right before I build the new figure/table. Now I wanted to try and see if I can have a global ColumnDataSource so that I can adjust the data via a CDSView (no need to replace table/figure then).

不幸的是,即使保留单独的CDS以及表和图的视图也失败.多次单击单选按钮时,出现以下javascript错误:未捕获的TypeError:无法读取未定义的属性'data'

Unfortunately even keeping a separate CDS and view for table and plot fails. When clicking the radio button a couple of times I receive the following javascript error: 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)

任何提示都非常感谢!

推荐答案

现在我想尝试看看是否可以有一个全局的ColumnDataSource,这样我可以通过CDSView调整数据(无需更换表格/数字).

Now I wanted to try and see if I can have a global ColumnDataSource so that I can adjust the data via a CDSView (no need to replace table/figure then).

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

The code you are showing is the one in which you are trying to replace the figure and table.

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

When you replace the child of a layout object in that way, you are not actually removing the previous figures from curdoc, and other elements in the document still have the old figures and tables in their references.

您可以尝试类似的方法直接更新源.

You could try something like that to update the sources directly.

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()

我喜欢这样做的是,您可以将 my_data 词典保存为numpy,稍后再加载并继续从那里更改绘图.

what I like about doing this is that you can just save the my_data dictionary with numpy, load it later and keep changing your plots from there.

def load_data():

    global my_data

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

    make_doc()

您可能可以使用pandas数据框执行类似的操作,不过我对普通字典比较满意.

You can probably do something similar using pandas dataframes, I am just more comfortable with plain dictionaries.

这篇关于使用全局ColumnDataSource时替换布局中的图形和表格的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆