使用CDSView绘制多行字形

时间:2018-06-13 20:25:06

标签: python bokeh

我的目标是在CDS中为每个类别(案例)绘制一行字形。用户可以选择不同情况下的不同数据集,因此数据集1得到案例A,B和数据集2得到A,B,C,D,...

将数据集加载到df中,按摩数据并进入CDS。然后我循环案例并使用CDSView过滤源并在源中绘制每个案例的一行字形。我正在使用一个虚拟人物来描绘情节之外的图例,并在此过程中创建/更新此图例。

初始数据集工作正常,但是当用户选择具有不同数量的案例的新数据集时,我很困难。然后我再次循环以创建与新数据集中的案例匹配的新行字形;还需要更新虚拟人物中的传说(这里我也很挣扎,因为我最终将一个传奇放在另一个传奇上)。

最终耗费时间;对于我使用的数据集(不在下面的示例中),图例条目一次显示一行,非常慢。所以,我肯定做错了什么。

任何帮助表示感谢,谢谢!

注意:使用线条字形而不是multi_line的原因是我想使用交互式图例来隐藏/取消隐藏线条。

import pandas as pd
import numpy as np

from bokeh.palettes import Spectral8
from bokeh.plotting import figure 
from bokeh.models import ColumnDataSource, CDSView, GroupFilter, Legend, LegendItem
from bokeh.models import GlyphRenderer
from bokeh.models.widgets import Select
from bokeh.io import curdoc
from bokeh.layouts import row, widgetbox, column

data1 = { 'case' : ['A']*4 + ['B']*4,
          'x' : [1,2,3,4] * 2, 
          'val1' : [1,2,3,4] + [3,1,3,2],
          'val2' : [11,6,23,14] + [13,31,5,12]}

data2 = { 'case' : [item for pair in zip(list('ABCDEFGH'),list('ABCDEFGH')) for item in pair] ,
          'x' : [1,5] * 8,
          'val1' : [item for pair in zip(range(1,9),range(2,10)) for item in pair],
          'val2' : [item for pair in zip(range(25,33),range(22,30)) for item in pair]}

df1= pd.DataFrame(data1)
df2= pd.DataFrame(data2)
colors = Spectral8
src = ColumnDataSource({'x':[], 'value':[], 'case':[]})

# global dict for df and color
glb_data = {'df' : [], 'color': []}

p = figure(plot_width=800, plot_height=350)

# dumfig is just figure used for legend
dumfig = figure(width = 250, height = 350,
            toolbar_location = None, outline_line_alpha = 0)
dumfig.x_range.end = 0 
dumfig.x_range.start = 1 
dumfig.grid.grid_line_color = None
dumfig.axis.visible = False


def update_dataset(attr, old, new):
    # update global df with selected dataset
    glb_data['df'] = eval('df'+new)
    cases = glb_data['df']['case'].unique().tolist()
    glb_data['color'] = {case : colors[i] for i,case in enumerate(cases)}

    update_src()

    update_line_glyphs()


def update_src():
    # update line source, need to rename chosen column to 'value'
    df = glb_data['df'].copy()
    drp_col = 'val1'
    rename_col = 'val2'
    if select_prop.value == 'val1':
        drp_col = 'val2'
        rename_col = 'val1'

    df = df.drop(drp_col, axis = 1 )
    df = df.rename(columns={rename_col: 'value'})

    src.data = ColumnDataSource(df).data


def update_line_glyphs():
    cases = glb_data['df']['case'].unique().tolist()
    rend_remove = []
    if dumfig.legend != []:
        dumfig.renderers.remove(dumfig.legend[0])
        for rend in dumfig.renderers:
            if type(rend) == GlyphRenderer:
                dumfig.renderers.remove(rend)

    line_glyphs = []
    leg_items = []
    for case in cases:
        view = CDSView(source=src,
               filters=[GroupFilter(column_name='case', group=case)])

        r_glyph = p.line(x='x',
                         y='value',
                         line_width=2,
                         source = src,
                         color = glb_data['color'][case],
                         view=view)
        leg_items.append(LegendItem(label = case, renderers = [r_glyph]))
        line_glyphs.append(r_glyph)

    # update legend in dumfig
    dumfig.renderers.extend(line_glyphs)
    dumfig.renderers.append(Legend(items = leg_items, location = 'top_left'))


select_dataset=Select(title = 'Dataset',
                      options = ['1','2'],)

select_dataset.on_change('value', update_dataset)

select_prop=Select( title = 'Plot property',
                    options = ['val1','val2'],
                    value = 'val1')

select_prop.on_change('value', lambda attr, old, new: update_src())

select_dataset.value = select_dataset.options[0]

widgets = widgetbox(select_dataset, select_prop)

curdoc().add_root(row(widgets,p,dumfig))

0 个答案:

没有答案