Bokeh - невозможно обновить ColumnDataSource при изменении в выпадающем меню - PullRequest
0 голосов
/ 23 мая 2019

У меня проблемы с обновлением базового ColumnDataSource при выборе нового значения из раскрывающегося списка. В разделе «update_data» я изменяю значения базового ColumnDataSource для своего графика. Столбики ошибок обновляются на графике, но данные графика не изменяются. Я упростил код ниже. Любая идея, как обновить все данные, а не только панели ошибок?

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))

1 Ответ

0 голосов
/ 23 мая 2019

Круги не обновляются, потому что вы на самом деле не настраиваете вызов к кругу для использования источника:

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

Когда вы передаете фактические списки / массивы как x, y и т. Д., Как вы делаете выше, тогда Bokeh создает новый CDS для внутреннего использования. Если вы хотите, чтобы глиф использовал источник, который вы передаете, вам действительно нужно передать его, и координаты должны ссылаться только на имена столбцов этого источника:

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

Кроме того, формат данных в вашем CDS не подходит для этого использования:

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

Как следует из названия, ColumnDataSource содержит столбцы . Значением каждого столбца должен быть одномерный массив или список , а не список списков, как у вас здесь (горстка «мульти» -глифов принимает это, но не кружок и т. Д.). То есть вам нужно будет иметь отдельный столбец для каждого круга, а не один столбец с «подсписками» для каждого круга.

...