面向对象的散景的多个问题[OBSOLETE]

时间:2014-10-03 08:57:16

标签: python bokeh




注意:这个问题涉及第一代" Bokeh服务器,已被弃用和删除了几年。此问题或其答案中的任何内容均与任何版本的Bokeh相关> = 0.11

有关使用现代支持的Bokeh服务器的详细信息,请参阅“用户指南”的Running a Bokeh Server章节。




我试图了解Bokeh我正在构建的互动应用。我正在查看Bokeh examples,我发现大多数示例都是在全局命名空间中编写的,但是" app"子目录以一种漂亮的,面向对象的风格编写,其中主类来自像HBox这样的Property类。

这将是一个混乱的问题,因为我不认为这种编程方式Bokeh有很好的记录。我遇到的第一件事是除非我包括extra_generated_classes,否则情节不会被绘制。

  1. extra_generated_classes做什么?

    其次,看起来事件循环setup_eventscreate之前启动时调用,并且随后每次情节触发事件时都会调用。

  2. 为什么每次触发事件时setup_events都需要注册回调?为什么在第一次尝试注册之前,它是否等待创建完成?

    我不确定的最后一件事是如何在这里强制重绘一个字形。滑块演示适用于我,我尝试基本上做同样的事情,除了散点图而不是线。

    我在update_data的最后设置了一个pdb跟踪,我可以保证self.source匹配self.plot.renderers[-1].data_source,并且两者都从一开始就进行了调整。但是,self.plot本身并没有改变。

  3. 面向对象的方法是什么相当于调用store_objects来更新绘图?

    我对第三个问题特别困惑,因为它看起来不像sliders_app示例需要这样的东西。为了澄清,我试图制作可变数量的小部件/滑块,所以这就是我的代码:

  4. 类属性:

    extra_generated_classes = [['ScatterBias', 'ScatterBias', 'HBox']]
    maxval = 100.0
    
    inputs = Instance(bkw.VBoxForm)
    outputs = Instance(bkw.VBoxForm)
    plots = Dict(String, Instance(Plot))
    source = Instance(ColumnDataSource)
    
    
    cols = Dict(String, String)
    widgets = Dict(String, Instance(bkw.Slider))
    # unmodified source
    df0 = Instance(ColumnDataSource)
    

    初始化方法

    @classmethod
    def create(cls):
        obj = cls()
    
        ##############################
        ## load DataFrame
        ##############################
        df = pd.read_csv('data/crime2013_tagged_clean.csv', index_col='full_name')
        obj.cols = {'x': 'Robbery', 
                'y': 'Violent crime total',
                'pop': 'Population'
                }
    
        cols = obj.cols
    
        # only keep interested values
        df2= df.ix[:, cols.values()]
    
        # drop empty rows
        df2.dropna(axis=0, inplace=True)
    
        df0 = df2.copy()
        df0.reset_index(inplace=True)
        # keep copy of original data
        obj.source = ColumnDataSource(df2)
        obj.df0 = ColumnDataSource(df0)
    
        ##############################
        ## draw scatterplot
        ##############################
    
        obj.plots = {
                'robbery': scatter(x=cols['x'],
                    y=cols['y'], 
                    source=obj.source,
                    x_axis_label=cols['x'],
                    y_axis_label=cols['y']),
                'pop': scatter(x=cols['pop'], 
                    y=cols['y'], 
                    source=obj.source,
                    x_axis_label=cols['pop'],
                    y_axis_label=cols['y'],
                    title='%s by %s, Adjusted by by %s'%(cols['y'], 
                        cols['pop'], cols['pop'])),
            }
    
        obj.update_data()
        ##############################
        ## draw inputs
        ##############################
        # bokeh.plotting.scatter 
        ## TODO: refactor so that any number of control variables are created
        # automatically. This involves subsuming c['pop'] into c['ctrls'], which
        # would be a dictionary mapping column names to their widget titles 
        pop_slider = obj.make_widget(bkw.Slider, dict(
                start=-obj.maxval, 
                end=obj.maxval, 
                value=0, 
                step=1, 
                title='Population'), 
            cols['pop'])
    
        ##############################
        ## make layout
        ##############################
        obj.inputs = bkw.VBoxForm(
                children=[pop_slider]
                )
    
        obj.outputs = bkw.VBoxForm(
                children=[obj.plots['robbery']]
            )
    
        obj.children.append(obj.inputs)
        obj.children.append(obj.outputs)
    
        return obj
    

    update_data

    def update_data(self):
        """Update y by the amount designated by each slider"""
        logging.debug('update_data')
        c = self.cols
        ## TODO:: make this check for bad input; especially with text boxes
        betas = { 
                varname: getattr(widget, 'value')/self.maxval 
                for varname, widget in self.widgets.iteritems()
                }
    
        df0 = pd.DataFrame(self.df0.data)
        adj_y = []
        for ix, row in df0.iterrows():
            ## perform calculations and generate new y's
            adj_y.append(self.debias(row))
    
        self.source.data[c['y']] = adj_y
        assert len(adj_y) == len(self.source.data[c['x']])
        logging.debug('self.source["y"] now contains debiased data')
    
        import pdb; pdb.set_trace()
    

    请注意,我确信事件处理程序已正确设置并触发。我只是不知道如何使更改后的源数据反映在散点图中。

1 个答案:

答案 0 :(得分:3)

我正在寻找相同的答案(缺乏文档使其变得困难)。

回答问题#1,“extra_generated_classes”的效用是什么:

tl; dr extra_generated_classes定义模板生成js / html代码中使用的模块名,类名和父名,并扩展传递给app类的父类(在示例中通常为HBox或VBox) 。

更长的答案。查看bokeh / server / utils / plugins.py中的源代码,这是使用--script命令行参数传递给bokeh-server的代码上运行的代码。在plugins.py结束时,您可以看到extra_generated_classes被传递给flask方法render_template,该方法呈现Jinja2模板。在模板中查看oneobj.html,extra_generated_classes是一个包含三个数组的数组的数组:modulename,classname和parentname,它们被传递到bokeh.server.generatejs:

{% block extra_scripts %}
  {% for modulename, classname, parentname in extra_generated_classes %}
  <script
    src="{{ url_for('bokeh.server.generatejs', modulename=modulename, classname=classname, parentname=parentname) }}"
  ></script>
  {% endfor %}
{% endblock %}

bokeh.server.generatejs是bokeh / server / views / plugins.py中的Python代码,只调用render_template作为模板app.js,您可以在散景/服务器/模板中找到它。此模板采用modulename,classname和parentname,并基本上创建js代码,将父名称(例如HBox或VBox)扩展为类名(您的应用程序)。