散景:回调时,新图覆盖在旧图之上

Bokeh: Upon callback, the new plot is overlaid on top of the old plot

我正在尝试嵌入我的情节。我有 Select 用于 x-axis 和 y-axis 选择的小部件。当我更改 y-axis 时,情节会按预期更新(图 1);但是,更改 x-axis 给我带来了问题。 更改 x-axis 选项后,新折线图会覆盖在旧图之上(图 2)。只有当我刷新页面时,图形才会按预期呈现(图 3)。如何在不刷新整个页面的情况下刷新我的情节?

这是我正在尝试的工作示例:

import io
import pandas as pd

from bokeh.plotting import figure
from bokeh.layouts import row, column
from bokeh.models import ColumnDataSource, Range1d
from bokeh.models.widgets import Select
from bokeh.client import push_session
from bokeh.document import Document
from bokeh.embed import server_session

document = Document()
session = push_session(document)

trip_data = [{"trip": '1', "totalDistanceTravelled": "4.66", "totalTimeTaken": "765083", "time": "1504189219256",
              "carRegNo": "ABC123", "driverName": "Anne"},
             {"trip": '2', "totalDistanceTravelled": "14.63", "totalTimeTaken": "1282369", "time": "1504189219256",
              "carRegNo": "DEF345", "driverName": "Harry"},
             {"trip": '3', "totalDistanceTravelled": "3.66", "totalTimeTaken": "602713", "time": "1504189219256",
              "carRegNo": "XYZ890", "driverName": "Ron"},
             {"trip": '4', "totalDistanceTravelled": "7.11", "totalTimeTaken": "2234282", "time": "1504189219256",
              "carRegNo": "ABC123", "driverName": "Anne"},
             {"trip": '5', "totalDistanceTravelled": "14.14", "totalTimeTaken": "1282369", "time": "1504189219256",
              "carRegNo": "DEF345", "driverName": "Harry"},
             {"trip": '6', "totalDistanceTravelled": "4.33", "totalTimeTaken": "748446", "time": "1504189219256",
              "carRegNo": "DEF345", "driverName": "Harry"},
             {"trip": '7', "totalDistanceTravelled": "10.66", "totalTimeTaken": "960362", "time": "1504189219256",
              "carRegNo": "XYZ890", "driverName": "Ron"}]

df = pd.DataFrame(trip_data)
drivers = df['driverName'].str.strip()
vehicles = df['carRegNo'].str.strip()
time_stamp = df['time'].astype(float)
total_distance_travelled = df['totalDistanceTravelled'].astype(float)
df['totalTimeTaken'] = df['totalTimeTaken'].astype(float)
df['totalTimeTaken'] /= 1000 * 3600

# Create Input controls
x_axis = Select(title="X Axis", options=sorted(["Drivers", "Vehicle Reg. Number"]), value="Drivers")
y_axis = Select(title="Y Axis", options=sorted(["Distance Travelled (kms)", "Time Taken (hours)"]),
                value="Time Taken (hours)")
source = ColumnDataSource(data=dict(x=[], y=[]))
p = figure(x_range=[], y_range=Range1d(), plot_height=600, plot_width=700, title="")

def update():
    print "UPDATE"
    col_key_values = {
        "Time Taken (hours)": 'totalTimeTaken',
        "Distance Travelled (kms)": 'totalDistanceTravelled'
    }

    x_map = {
        "Drivers": drivers,
        "Vehicle Reg. Number": vehicles
    }

    x_name = x_map[x_axis.value]
    x_y_values = {}
    for x in x_name.unique():
        x_y_values[x] = round(df.loc[x_name == x, col_key_values[y_axis.value]].astype(float).sum(), 2)
    source.data = dict(
        x=list(x_y_values.keys()),
        y=list(x_y_values.values())
    )
    p.y_range.start = min(x_y_values.values())-1
    p.y_range.end = max(x_y_values.values())+1
    p.xaxis.axis_label = x_axis.value
    p.yaxis.axis_label = y_axis.value
    p.x_range.factors = list(x_y_values.keys())
    p.line(x="x", y="y", source=source)
    print source.data
    return p

def on_x_change(attr, old, new):
    global x_axis
    x_axis.value = new
    update()


def on_y_change(attr, old, new):
    global y_axis
    y_axis.value = new
    update()


def create_layout():
    print 'CREATE LAYOUT'
    x_axis.on_change('value', on_x_change)
    y_axis.on_change('value', on_y_change)
    controls = column(children=[x_axis, y_axis])
    layout = column(children=[controls, update()])
    return layout


layout = create_layout()

html = u"""
<html>
    <head></head>
    <body>
        %s
    </body>
</html>
""" % server_session(layout, session_id=session.id, relative_urls=False)

with io.open("sample.html", mode='w+', encoding='utf-8') as f:
    f.write(html)

print(__doc__)

document.add_root(layout)

if __name__ == "__main__":
    print("\npress ctrl-C to exit")
    session.loop_until_closed()

附件:

与其使用 p.line() 重画线条,不如使用 source.data.update(dict) 更新线条的源代码。只是有一个空源在没有数据的开头画线。

更新图形范围后也更新源

p = figure(x_range=[], y_range=Range1d(), plot_height=600, plot_width=700, title="")
source = ColumnDataSource(data={'x':[],'y':[]})
p.line(x="x", y="y", source=source)

def update():
    print "UPDATE"
    col_key_values = {
        "Time Taken (hours)": 'totalTimeTaken',
        "Distance Travelled (kms)": 'totalDistanceTravelled'
    }

    x_map = {
        "Drivers": drivers,
        "Vehicle Reg. Number": vehicles
    }

    x_name = x_map[x_axis.value]
    x_y_values = {}
    for x in x_name.unique():
        x_y_values[x] = round(df.loc[x_name == x, col_key_values[y_axis.value]].astype(float).sum(), 2)
    p.y_range.start = min(x_y_values.values())-1
    p.y_range.end = max(x_y_values.values())+1
    p.xaxis.axis_label = x_axis.value
    p.yaxis.axis_label = y_axis.value
    p.x_range.factors = list(x_y_values.keys())
    source.data.update( dict(
        x=list(x_y_values.keys()),
        y=list(x_y_values.values())
    ))