【问题标题】:How to reuse a Bokeh App with different data on an embedded Bokeh Server如何在嵌入式 Bokeh 服务器上重用具有不同数据的 Bokeh App
【发布时间】:2017-11-04 09:42:24
【问题描述】:

我已将用户指南的Embedding Bokeh Server as a Library 部分中的flask_embed.html 中的简单散景应用示例集成到我的烧瓶应用程序中,只是为了验证我可以正确设置集成到我的烧瓶应用程序所需的服务器基础架构,并且我可以成功地路由到它而没有任何阻碍。

我看不清楚的是如何摆脱散景应用程序和对使用罐装数据的modify_doc 全局函数的依赖之间的硬关联,该函数的签名必须是散景文档的单个参数.我想将数据从我的模型推送到一个函数中,然后 Bokeh 将使用该函数呈现文档。有一个警告可能会或可能无关紧要,但模型仅由用户在运行时填充,当他们加载由 Flask 应用程序和 Flask 会话维护的 csv 文件时。

作为一个最小的例子,我的烧瓶应用程序维护了一个模型实例,其中包含一个 pandas DataFrame,我希望创建一个 Bokeh 应用程序,我可以重用来绘制不同列对的散点图的数据框。这些对通过 Flask 创建的链接呈现给用户,这些链接当前指向具有通过 matplotlib 生成的静态图像的页面。这些图像是由一个函数创建的,该函数将用于生成文件名的列名和 x 和 y 坐标作为数据框的列。列名作为参数传递给烧瓶路由,这些路由会向下渗透到获取适当图像的模板。

基本上,我想以某种功能方式在 Bokeh 中的不同数据上重用相同的绘图逻辑,就像我对 Matplotlib 所做的那样。似乎FunctionHandler 技术固定在数据源上,因为我试图创建一个维护状态的类,该类实现了modify_doc 方法,但问题在于类中所需的self 参数。

class BokehParamPlot() :
    def __init__( self , paramName , x , y ) :   
        # Initialize member data
        self.paramName = paramName
        self.x         = x
        self.y         = y

    def modify_doc( self , doc ) :
        x = self.x
        y = self.y

        source = ColumnDataSource(data=dict(x=x, y=y))

        plot = figure()
        plot.line('x', 'y', source=source)
        doc.add_root(column(slider, plot))

上述尝试的精神是尝试对FunctionHandler 技术进行参数化,因为应用程序将与不同的数据相同,并且我不想为我的数据框中的每一对数据创建一个应用程序.但是没有运气

  File "ParamPlot.py", line 362, in <module>
    bokeh_app = Application(FunctionHandler(BokehParamPlot.modify_doc))
  File "C:\Anaconda3\lib\site-packages\bokeh\application\handlers\function.py", line 11, in __init__
    _check_callback(func, ('doc',))
  File "C:\Anaconda3\lib\site-packages\bokeh\util\callback_manager.py", line 32, in _check_callback
    raise ValueError(error_msg % (", ".join(fargs), formatted_args))
ValueError: Callback functions must have signature func(doc), got func(self, doc)

查看 Bokeh 源代码,另一个有希望的路线是 CodeHandler,但我希望我正在尝试的不是一些遥远的用例,我只是错过了一些点或与 Base Python 或其他东西的一些连接这可能很快就会让我上路。

【问题讨论】:

    标签: python bokeh


    【解决方案1】:

    这确实可以通过将查询字符串传递给散景应用程序来实现。在这种重用场景中需要发现一个关键问题,那就是autoload_server 的session_id 参数的作用[编辑注意:此 session_id 功能仅适用于 Bokeh 12.4,12.5 破坏了此技术]。该会话 ID 在通过它传递的不同数据的各种文档呈现之间必须是不同的,因为如果您使用相同的 ID,则呈现的第一个数据将在尝试呈现时为其余数据缓存。如果需要将更多参数信息传递给文档,您可以插入script 标记的src 属性,该属性由autoload_server 返回,就像显示here。

    接下来是使模型数据对渲染散景文档的模块可见的方法。在我的情况下,我重新分层了我的 Flask 应用程序,以将我的用户会话模型存储在一个较低级别的模块中,该模块可访问烧瓶应用程序主脚本和我保存所有绘图逻辑的绘图模块。在我的 Flask 主页中,我执行以下逻辑以在 Flask 会话 cookie 中设置会话信息,并将 a 参数从 flask 传递到我的散景文档...

    MyFlaskApp.py

    @flaskApp.route( '/' , methods=['GET', 'POST'] )
    def index():
        # Create or load model instance for this user session based on model uuid cookie
        if sKeyModel in session :
            if session[ sKeyModel ] not in SessionInfo.userModels :
                logger.debug( "Loading Model instance for current user" )
                SessionInfo.userModels[ session[ sKeyModel ] ] = Model( ) # TODO create new for now, but latter must support loading from persisted JSON model file
        else :
            logger.debug( "Creating Model instance for current user" )
            session[ sKeyModel ] = str( uuid.uuid1() )
            SessionInfo.userModels[ session[ sKeyModel ] ] = Model( )
    
        model = SessionInfo.userModels[ session[ sKeyModel ] ]
    ...
    @flaskApp.route( '/Plots/<param>' , methods=['GET'] )
    def interactivePlotsPage( param ) :     
        script = autoload_server( model      = None ,
                                  session_id = "%s.%s" % ( session[sKeyModel] , param ),
                                  url        = 'http://localhost:5006/' )
    
        return render_template( "plotsPage.html" , script=script )
    

    SessionInfo.py

    bokehSession = "bokeh-session-id"
    userModels = {} # GUID to user Model instances maintained in the Server
    

    Plots.py

    def modify_doc(doc) :
        args       = doc.session_context.request.arguments
        session_Id = str( args[SessionInfo.bokehSession][0].decode('utf-8') ).split(".")
        sessionId  = session_Id[0]
        param      = session_Id[1]
    
        logger.debug( "modify_doc %s" % param )
    
        model = SessionInfo.userModels[sessionId]
        # User now has access to the model and param to drill into the model to pull out arbitrary data to conduct plots with.
    

    对于我的应用程序来说,Flask 应用程序可以将所有模型数据保存在内存中,因为这只是我们用来分析一些机器学习异常值和性能的内部数据分析仪表板工具。

    使用查询字符串,我们可以向下传递参数以在 Bokeh 中访问,然后您基本上只需要访问您需要在 bokeh 文档中呈现的数据。用户组中有一个条目 (Bokeh Server + Flask : Passing data to plot from Flask to the Bokeh server app) 讨论了这种确切情况...

    围绕不同的信息来源,我也发现了这个 来自 Bryan Van de Ven:

    有很多方法可以做到这一点,也可以做“繁重的工作” 别处。您只需要传达结果/数据/无论 应用程序需要为了进行绘图,应用程序。但这可能是 以多种方式完成:

    • 应用可以访问 HTML 请求参数(新功能)
    • 应用可以访问 cookie
    • 应用可以访问外部数据库(redis、mongo等)中的数据
    • 应用可以对 REST API 进行 AJAX 调用
    • ...

    基于此,我认为可能的解决方案是:生成数据 要绘制(在 Flask 端)使用 pull_session() 获取会话 使用 session.id 作为 key 将数据临时存储在 redis 中 使用散景应用中的 session_id 从 redis 中获取数据

    我认为真正让您思考的是如何回答“我怎样才能使数据在全局范围内对这个函数可见?”这个问题。所以自然会想到较低级别的全局用户会话数据,数据库, 服务器的文件系统、文档应用所在的模块级状态信息等。

    我认为与 flask_embed.py 示例的差距在于关于如何将参数信息传达给散景文档的这一点使这些拼图太多,彼此之间的距离很远,这使得想象这一切有点令人困惑一起工作。谢天谢地,我的参数化文档完全按照我的需要工作,我们在 Bokeh 领域飞得很高,到目前为止,我们的仪表板给我留下了深刻的印象。

    【讨论】:

    猜你喜欢
    • 2015-02-15
    • 2021-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多