简体   繁体   English

Bokeh - 在下拉菜单中更改时无法更新ColumnDataSource

[英]Bokeh - unable to update ColumnDataSource upon change in dropdown menu

I'm having issues with updating an underlying ColumnDataSource when selecting a new value from a dropdown. 从下拉列表中选择新值时,我遇到了更新基础ColumnDataSource的问题。 In the "update_data" section, I am changing the values of the underlying ColumnDataSource for my plot. 在“update_data”部分中,我正在更改我的绘图的基础ColumnDataSource的值。 The error bars get updated on the plot, but the plotted data does not change. 错误条在图上更新,但绘制的数据不会更改。 I've simplified the code below. 我简化了下面的代码。 Any idea how to update all of the data, and not just the error bars? 知道如何更新所有数据,而不仅仅是错误栏?

from bokeh.layouts import row, column
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Slider, TextInput
from bokeh.plotting import figure
from bokeh.io import output_file, show
from bokeh.models.widgets import Dropdown
import pandas as pd
import numpy as np
from bokeh.io import output_file, show, curdoc
from bokeh.layouts import row, column, widgetbox
from bokeh.models import (
    ColumnDataSource,
    HoverTool,
    LinearColorMapper,
    BasicTicker,
    PrintfTickFormatter,
    ColorBar,
    Legend,
    Whisker,
)
from bokeh.models.widgets import PreText, Select, RadioGroup, TextInput
from bokeh.plotting import figure
from bokeh.transform import dodge, factor_cmap
import bokeh.plotting
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.widgets import Button
import io
import base64
import random
import statistics as stat
from bokeh.models.tickers import SingleIntervalTicker
from bokeh.plotting import figure, show




def sectionize(df, rows, cols):
    rowWise = df.stack()
    colWise = df.transpose().stack()
    rowData = []
    colData = []
    for x in rows:
        rowData.append(list(rowWise[x]))
    for x in range(1, (cols + 1)):
        colData.append(list(colWise[x]))
    print("sectionize has occured")
    return rowData, colData

def getLowerUpper(data):
    lower, upper = [], []
    for x in data:
        if x:
            mean = stat.mean(x)
            std = stat.stdev(x)
            lower.append(mean - std)
            upper.append(mean + std)
        else:
            lower.append(0)
            upper.append(0)
    return lower, upper

def sectionizePlot(source, source_error, type, base):
    print("sectionize plot created with typ: " + type)
    colors = []
    for x in range(0, len(base)):
        colors.append(getRandomColor())
    title = type + "-wise Intensity Distribution"
    p = figure(plot_width=600, plot_height=300, title=title)
    p.add_layout(
        Whisker(source=source_error, base="base", upper="upper", lower="lower"))
    for i, sec in enumerate(source.data['base']):
        p.circle(x=source_error.data["base"][i], y=sec, color=colors[i])
    p.xaxis.axis_label = type
    p.yaxis.axis_label = "Intensity"
    if (type.split()[-1] == "Row"):
        print("hit a row")
        conv = dict(enumerate(list("nABCDEFGHIJKLMNOP")))
        conv.pop(0)
        p.xaxis.major_label_overrides = conv
    p.xaxis.ticker = SingleIntervalTicker(interval=1)
    print("sectionizePlot changed")
    return p

def getRandomColor():
    colors = ['aqua', 'aquamarine', 'black', 'blue', 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse', 'chocolate',
         'coral', 'cornflowerblue', 'crimson', 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen',
         'darkgrey', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon',
         'darkseagreen', 'darkslateblue', 'darkslategray', 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink',
         'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick', 'forestgreen', 'fuchsia', 'gold', 'goldenrod',
         'gray', 'green', 'greenyellow', 'grey', 'hotpink', 'indianred', 'indigo', 'khaki', 'lavender', 'lawngreen', 'lime',
         'limegreen', 'magenta', 'maroon', 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple',
         'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 'mediumturquoise', 'mediumvioletred', 'midnightblue',
         'navy', 'olive', 'olivedrab', 'orange', 'orangered', 'orchid', 'peachpuff', 'peru', 'pink', 'plum', 'powderblue',
         'purple', 'red', 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'sienna', 'silver',
         'skyblue', 'slateblue', 'slategray', 'slategrey', 'springgreen', 'steelblue', 'tan', 'teal', 'thistle', 'tomato',
              'turquoise', 'violet', 'yellow', 'yellowgreen']
    return colors[random.randint(0, 101)]


colBase = list(range(1, 3))
colData = [[1,2,3,4,5,6], [7, 8, 9, 10, 11, 12]]
colData_lower, colData_upper = getLowerUpper(colData)
colSectTotSource = ColumnDataSource(data=dict(base=[]))
colSectTotSource_error = ColumnDataSource(data=dict(base=[], lower=[], upper=[]))
colSectTotSource.data = dict(base=colData)
colSectTotSource_error.data = dict(base=colBase, lower=colData_lower, upper=colData_upper)

menu = [("A", "A"), ("B", "B")]
dropdown = Dropdown(label="Dropdown button", button_type="warning", menu=menu)
colPlot = sectionizePlot(colSectTotSource, colSectTotSource_error, "Column", colBase)

def update_data(attrname, old, new):
    d = dropdown.value
    if(d == "B"):
        colData = [[11,12,13,14,15,16], [17,18,19,20,21,22]]
    else:
        colData = [[1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]]

    colData_lower, colData_upper = getLowerUpper(colData)
    #colSectTotSource = ColumnDataSource(data=dict(base=[]))
    colSectTotSource.data = dict(base=colData)
    colSectTotSource_error.data = dict(base=colBase, lower=colData_lower, upper=colData_upper)

for w in [dropdown]:
    w.on_change('value', update_data)

inputs = column(dropdown)
curdoc().add_root(row(inputs, colPlot, width=800))

The circles are not updating because you are not actually configuring the call to circle to use the source: 圆圈未更新,因为您实际上没有配置圆圈调用以使用源:

p.circle(x=source_error.data["base"][i], y=sec, color=colors[i])

When you pass actual lists/arrays as x , y , etc, as you are doing above, then Bokeh creates a new CDS to use internally. 当您将实际列表/数组作为xy等传递时,就像上面所做的那样,然后Bokeh创建一个新的CDS以在内部使用。 If you want a glyph to utilize a source you pass in, you actually have to pass it in, and the coordinates should only refer to the column names of that source: 如果您希望字形使用您传入的源,则实际上必须将其传入,并且坐标应仅引用该源的列名:

my_source = ColumnDataSource(data=dict(foo=[...], bar=[...]))
p.circle(x="foo", y="bar", source=my_source)

Additionally, the format of data in your CDS is not correct for this usage: 此外,CDS中的数据格式对于此用法不正确:

{'base': [[1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]]}

As the name suggests, the ColumnDataSource contains columns . 顾名思义,ColumnDataSource包含 The value of each column needs to be a one-dimensional array or list, not a list of lists as you have here (a handful of "multi"-glyphs accept this, but not circle, etc). 每列的值需要是一维数组或列表,而不是像这里一样的列表列表(少数“多”字形接受这个,但不是圆形等)。 Ie you will need to have a separate column for every circle, not one column with "sublists" for each circle. 也就是说,每个圆圈都需要一个单独的列,而不是每个圆圈都有一个带有“子列表”的列。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM