【问题标题】:Bokeh Python; Use Callback on ColumnDataSource to change Stacked Bar Chart with Select Widget散景 Python;在 ColumnDataSource 上使用回调以使用 Select Widget 更改堆积条形图
【发布时间】:2015-11-01 18:45:22
【问题描述】:

我正在尝试使用带有 bokeh(python) 的 java 脚本回调来在给定选择小部件值时更改堆叠条形图。我正在使用矩形字形来构建堆叠条形图。每个栏都是一个年龄组,我的小部件应该选择一个年龄组来显示。我需要做的就是告诉图表停止渲染其他矩形字形并将一个年龄组的 y 值更改为等于其高度(从而使其成为一个简单的条形图)。

我的所有数据都保存在数组中,然后我将其转换为列表以便将其传递给回调。我的 java 脚本警报告诉我我的 for 语句有效,并且它正在读取 y 值。我的回调逻辑可能很时髦/不正确,但我更担心字形根本不会响应回调而改变。也许这是我在回调中推送数据的方式?谢谢!!

from bokeh.models import  Callback, ColumnDataSource, Rect, Select
from bokeh.plotting import figure, output_file, show, VBox, gridplot, HBox
import pandas as pd


#Color Dictionary
redcolor5 = {u'All ages': "#720017", u'70+ years': "#bd0026", u'50-69 years':  "#f03b20", u'15-49 years': "#fd8d3c", u'Under 5 years': "#f4cc63", u'gridline': '#b2ada6', u'background': '#e3e0db', u'axis' : '#aba9a7'}

#Just a sample of my data
country_both = ['China', 'India', 'United States', 'Russia', 'Japan', 'Indonesia', 'Germany', 'United Kingdom', 'Italy', 'Brazil']
ages_gen = ['Under 5 years', '15-49 years', '50-69 years', '70+ years', 'All ages', 'Age-standardized']

height70yr = [919470, 421922, 321125, 193960, 148946, 107822, 97529, 90198, 81107, 76782]
height50to69 = [640496, 626995, 182338, 195472, 40422, 109242, 44161, 33333, 24964, 64429]
height15to49 = [126094, 139420, 26159, 43239, 5480, 39040, 6829, 4163, 3571, 16152]
heightUnder5 = [10210, 43338, 82, 714, 41, 5255, 0, 26, 0, 1201]

frame = pd.DataFrame({"arBoth_70yr_Cnty": height70yr, "arBoth_5069yr_Cnty": height50to69, "arBoth_1549yr_Cnty": height15to49, "arBoth_5yr_Cnty": heightUnder5})
arBoth_70yr_Cnty = frame['arBoth_70yr_Cnty'] 
arBoth_5069yr_Cnty = frame['arBoth_5069yr_Cnty']
arBoth_1549yr_Cnty = frame['arBoth_1549yr_Cnty']
arBoth_5yr_Cnty = frame['arBoth_5yr_Cnty']

#Y Values for Stacked bar chart
yUnder5 = (arBoth_5yr_Cnty/2).tolist()
y15to49 = (arBoth_5yr_Cnty+arBoth_1549yr_Cnty/2).tolist()
y50to69 = (arBoth_5yr_Cnty+arBoth_1549yr_Cnty+arBoth_5069yr_Cnty/2).tolist()
y70yr = (arBoth_5yr_Cnty+arBoth_1549yr_Cnty+arBoth_5069yr_Cnty+arBoth_70yr_Cnty/2).tolist()


output_file('UW_TobaccoDeath.html')

#Figure for Stacked bar chart
p1 = figure(title="Top Countries with Death Due to Tobacco by Age", 
            x_range=country_both, y_range=[0, max(arBoth_5yr_Cnty+arBoth_1549yr_Cnty+arBoth_5069yr_Cnty+arBoth_70yr_Cnty)],
            background_fill=redcolor5['background'], 
            plot_width=700, plot_height = 400,
            outline_line_color= None)


#source for callback
source1 = ColumnDataSource(data=dict(x=country_both, yUnder5 = yUnder5, heightUnder5 = heightUnder5, y15to49 = y15to49, height15to49 = height15to49, y50to69 = y50to69, height50to69 = height50to69, y70yr=y70yr, height70yr = height70yr))

#Use rect glyphs for stached bars
p1.rect(x ='x', y ='yUnder5', width =.8, height = 'heightUnder5', source = source1, color=redcolor5['Under 5 years'], alpha=0.8, name = "Under 5")
p1.rect(x = 'x', y ='y15to49', width = .8, height ='height15to49', source = source1, color=redcolor5['15-49 years'], alpha=0.8, name = "15 to 49")
p1.rect(x = 'x', y ='y50to69', width = .8, height ='height50to69', source = source1, color=redcolor5['50-69 years'], alpha = .8, name = "50 to 69")
p1.rect(x = 'x', y ='y70yr', width = .8, height ='height70yr', source = source1, color=redcolor5['70+ years'], alpha = .8, name = "70+ yrs")


#Java script Callbacks for age  
#I want this to recognize the 70+ year old drop down selection 
#and change the plot so that the height of the glyph is the same as the y value and the 70 year old glyph is the only one that displays
Callback_Age = Callback(args=dict(source1 = source1), code="""
        var f = cb_obj.get('value');
        var data = source1.get('data');
        var x = data['x'];

        if (f == '70+ years') {
            var y = 0;
            var height = data['height70yr'];
            alert(height);
            }

        data['x'].push(x);
        data['y'].push(y);
        data['height'].push(height);
        source1.trigger('change');

    """)

#Use the Select widget
dropdown_age = Select(title="Ages:", value=ages_gen[4], options= ages_gen,  callback = Callback_Age)

#Display data
filters = VBox(dropdown_age)
tot =  HBox(filters, gridplot([[p1]]))
show(tot)

【问题讨论】:

    标签: javascript python callback data-visualization bokeh


    【解决方案1】:

    感谢您研究与烟草相关的伤亡情况!

    我的做法是使用 4 个来源(每个年龄组 1 个),然后根据所选年龄组手动更改它们。如果选择 15-49 年龄组,则关联源 y 居中(到身高 / 0),所有其他人的身高设置为 0。我已经完成了“所有年龄”组,但没有进行“年龄标准化”。而且我也没有尝试调整数字以更好地看到 5 岁以下组。 哦,我也换成了 numpy 数组。这只是一件方便的事情,如果您愿意,可以继续使用 Python 列表和 Panda 框架。

    如果您还有其他问题,可以问我, 蒂埃里

    from bokeh.models import  Callback, ColumnDataSource, Rect, Select,CustomJS
    from bokeh.plotting import figure, output_file, show,  gridplot
    from bokeh.models.widgets.layouts import VBox,HBox
    import numpy as np
    
    
    #Color Dictionary
    redcolor5 = {u'All ages': "#720017", u'70+ years': "#bd0026", u'50-69 years':  "#f03b20", u'15-49 years': "#fd8d3c", u'Under 5 years': "#f4cc63", u'gridline': '#b2ada6', u'background': '#e3e0db', u'axis' : '#aba9a7'}
    
    #Just a sample of my data
    country_both = ['China', 'India', 'United States', 'Russia', 'Japan', 'Indonesia', 'Germany', 'United Kingdom', 'Italy', 'Brazil']
    ages_gen = ['Under 5 years', '15-49 years', '50-69 years', '70+ years', 'All ages', 'Age-standardized']
    
    height70yr = np.array([919470, 421922, 321125, 193960, 148946, 107822, 97529, 90198, 81107, 76782])
    height50to69 = np.array([640496, 626995, 182338, 195472, 40422, 109242, 44161, 33333, 24964, 64429])
    height15to49 = np.array([126094, 139420, 26159, 43239, 5480, 39040, 6829, 4163, 3571, 16152])
    heightUnder5 = np.array([10210, 43338, 82, 714, 41, 5255, 0, 26, 0, 1201])
    zeros = np.zeros(len(country_both))
    
    
    #Y Values for Stacked bar chart
    yUnder5 = heightUnder5 / 2.0
    y15to49 = yUnder5 + height15to49/2.0
    y50to69 = y15to49 + height50to69/2.0
    y70yr = y50to69 + height70yr/2.0
    
    
    output_file('UW_TobaccoDeath.html')
    
    #Figure for Stacked bar chart
    p1 = figure(title="Top Countries with Death Due to Tobacco by Age", 
                x_range=country_both, y_range=[0, np.amax([y70yr+height70yr])],
                background_fill=redcolor5['background'], 
                plot_width=700, plot_height = 600,
                outline_line_color= None)
    
    
    #source for callback
    source1 = ColumnDataSource(data=dict(x=country_both, y = yUnder5, y_full = yUnder5, height = heightUnder5, height_full = heightUnder5 ,height_zeros = zeros, y_zeros = heightUnder5 / 2.0))
    source2 = ColumnDataSource(data=dict(x=country_both, y = y15to49, y_full = y15to49, height = height15to49, height_full = height15to49,height_zeros = zeros, y_zeros = height15to49 / 2.0))
    source3 = ColumnDataSource(data=dict(x=country_both, y = y50to69, y_full = y50to69, height = height50to69, height_full = height50to69,height_zeros = zeros , y_zeros = height50to69 / 2.0))
    source4 = ColumnDataSource(data=dict(x=country_both, y = y70yr, y_full = y70yr, height = height70yr, height_full = height70yr,height_zeros = zeros, y_zeros =  height70yr / 2.0))
    
    #Use rect glyphs for stached bars
    p1.rect(x ='x', y ='y', width =.8, height = 'height', source = source1, color=redcolor5['Under 5 years'], alpha=0.8, name = "Under 5")
    p1.rect(x = 'x', y ='y', width = .8, height ='height', source = source2, color=redcolor5['15-49 years'], alpha=0.8, name = "15 to 49")
    p1.rect(x = 'x', y ='y', width = .8, height ='height', source = source3, color=redcolor5['50-69 years'], alpha = .8, name = "50 to 69")
    p1.rect(x = 'x', y ='y', width = .8, height ='height', source = source4, color=redcolor5['70+ years'], alpha = .8, name = "70+ yrs")
    
    #Java script Callbacks for age  
    #I want this to recognize the 70+ year old drop down selection 
    #and change the plot so that the height of the glyph is the same as the y value and the 70 year old glyph is the only one that displays
    Callback_Age = CustomJS(args={'source1': source1,'source2': source2,'source3': source3,'source4': source4}, code="""
            var f = cb_obj.get('value');
            var data1 = source1.get('data');
            var data2 = source2.get('data');
            var data3 = source3.get('data');
            var data4 = source4.get('data');
            if (f == 'Under 5 years') {
                data3['height'] = data3['height_zeros'];
                data2['height'] = data2['height_zeros'];
                data4['height'] = data4['height_zeros'];
                data1['y'] = data1['y_zeros'];
                data1['height'] = data1['height_full'];
                source1.trigger('change');
                source2.trigger('change');
                source3.trigger('change');
                source4.trigger('change');
                }
            if (f == '15-49 years') {
                data1['height'] = data1['height_zeros'];
                data3['height'] = data3['height_zeros'];
                data4['height'] = data4['height_zeros'];
                data2['y'] = data2['y_zeros'];
                data2['height'] = data2['height_full'];
                source1.trigger('change');
                source2.trigger('change');
                source3.trigger('change');
                source4.trigger('change');
                }
    
            if (f == '50-69 years') {
                data1['height'] = data1['height_zeros'];
                data2['height'] = data2['height_zeros'];
                data4['height'] = data4['height_zeros'];
                data3['y'] = data3['y_zeros'];
                data3['height'] = data3['height_full'];
                console.log('data3',data3)
                source1.trigger('change');
                source2.trigger('change');
                source3.trigger('change');
                source4.trigger('change');
                }
            if (f == '70+ years') {
                data1['height'] = data1['height_zeros'];
                data2['height'] = data2['height_zeros'];
                data3['height'] = data3['height_zeros'];
                data4['y'] = data4['y_zeros'];
                data4['height'] = data4['height_full'];
                source1.trigger('change');
                source2.trigger('change');
                source3.trigger('change');
                source4.trigger('change');
                }
            if (f == 'All ages') {
                data1['height'] = data1['height_full'];
                data1['y'] = data1['y_full'];
                data2['height'] = data2['height_full'];
                data2['y'] = data2['y_full'];
                data3['height'] = data3['height_full'];
                data3['y'] = data3['y_full'];
                data4['height'] = data4['height_full'];
                data4['y'] = data4['y_full'];
                source1.trigger('change');
                source2.trigger('change');
                source3.trigger('change');
                source4.trigger('change');
                }
    
    
    
        """)
    #Use the Select widget
    dropdown_age = Select(title="Ages:", value=ages_gen[4], options= ages_gen,  callback = Callback_Age)
    
    #Display data
    filters = VBox(dropdown_age)
    tot =  HBox(filters, gridplot([[p1]]))
    show(tot)
    

    【讨论】:

    • 创建使用 CustomJS。一个问题。你能为颜色(年龄组)添加一个图例吗?
    猜你喜欢
    • 1970-01-01
    • 2021-01-01
    • 2020-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-07
    • 1970-01-01
    • 2017-11-02
    相关资源
    最近更新 更多