Обновление всплывающей подсказки карты Bokeh с помощью выбора или ползунка - PullRequest
0 голосов
/ 24 марта 2020

Я пытаюсь обновить всплывающую подсказку карты мира, используя слайсер или выпадающий список. У меня следующий вопрос, который отсортировал большую часть материала для пользовательского Bokeh Slider JS callback


import pandas as pd
import random
from datetime import timedelta

df = pd.DataFrame({'base' : ["2017-01-01" for t in range(10000)],
    'Date' : [random.randint(0, 1035) for t in range(10000)], 
                   'Sales' : [random.random() for t in range(10000)]})
df['base'] = pd.to_datetime(df['base'])
df["Date2"] = df.apply(lambda x: x["base"] + timedelta(days=x['Date']), axis=1)
df.drop(['base', 'Date'], axis=1, inplace=True)
df.set_index('Date2', inplace=True)
df['month'] = df.index.month
df['year'] = df.index.year
df['day'] = df.index.day
df.head()

from bokeh.models.widgets import Slider,Select
from bokeh.io import output_notebook, show, output_file

from bokeh.layouts import widgetbox, column
from bokeh.models import Slider, ColumnDataSource, CustomJS
from bokeh.plotting import figure, curdoc
from bokeh.core.properties import value
from bokeh.models.ranges import FactorRange
from bokeh.plotting import figure, output_file, show, ColumnDataSource
from bokeh.models import ColumnDataSource, CDSView, IndexFilter, BooleanFilter, HoverTool


source1=df.groupby(['year','month','day'], as_index = False).sum()
source = source1[source1['year']== 2017]
sourcex = source[source['month'] ==1]
Overall=ColumnDataSource(source)
Curr=ColumnDataSource(sourcex)
boolinit = source['month']==1
view = CDSView(source=Overall, filters=[BooleanFilter(boolinit)])
hover3 = HoverTool(tooltips = [('day', '@day'),('Sales','@{Sales}{0,0}')],
                   formatters = {'day': 'datetime','Sales': 'numeral'})

p =  figure(title='YEARLY SALES',  plot_width=600, plot_height=400, min_border=3,
tools = [hover3,'box_zoom','wheel_zoom', 'pan','reset'],  
toolbar_location="above")

r = p.vbar(x='day', top='Sales', width=0.2, color='#e8bc76', source=Curr)
p.xaxis.axis_label = 'Day'
p.xaxis.axis_label_text_font_style = 'normal'
p.xaxis.axis_label_text_font_size = '12pt'



callback = CustomJS(args=dict(source=Overall, sc=Curr), code="""       
        var f = select.value;
        sc.data['day'] = [];
        sc.data['Sales'] = [];
        for (var i = 0; i <= source.get_length(); i++){
          if (source.data['month'][i] == f){
            sc.data['day'].push(source.data['day'][i])
            sc.data['Sales'].push(source.data['Sales'][i])
          }
        }
        sc.change.emit();
    """)
select = Select(options=["1","2","3"], title="Month", callback=callback)
callback.args["select"] = select

layout = column(select, p)
#Display plot inline in Jupyter notebook
output_notebook()
output_file("Filterdata.html")
show(layout)

Теперь я скопировал то же самое для карты мира, как показано ниже:

import pandas as pd
import geopandas as gpd
current_week = 4
shapefile = 'data/countries_110m/ne_110m_admin_0_countries.shp'
gdf = gpd.read_file(shapefile)[['ADMIN', 'ADM0_A3', 'geometry']]
gdf.columns = ['country', 'country_code', 'geometry']
gdf = gdf.drop(gdf.index[159])
df = pd.DataFrame({'Country':['India','India'],
              'SalesGain':['10%','20%'],
                   'Week':[4,5],
                   'Color':[0.2,0.4]
             })

import json
from bokeh.models.widgets import Slider,Select
from bokeh.io import output_notebook, show, output_file
from bokeh.layouts import widgetbox, column
from bokeh.models import Slider, ColumnDataSource, CustomJS
from bokeh.plotting import figure, curdoc
from bokeh.core.properties import value
from bokeh.models.ranges import FactorRange
from bokeh.palettes import brewer
from bokeh.plotting import figure, output_file, show, ColumnDataSource
from bokeh.models import ColumnDataSource, CDSView, IndexFilter, BooleanFilter, HoverTool,GeoJSONDataSource, LinearColorMapper, ColorBar

from bokeh.plotting import figure, output_file, show
output_file("worldmap.html")


merged = gdf.merge(df, left_on = 'country', right_on = 'Country', how = 'left')
merged_json = json.loads(merged.to_json())
json_data = json.dumps(merged_json)
geosource_all = GeoJSONDataSource(geojson =  json_data)

df_curr = df[df['Week']==current_week]
merged_curr = gdf.merge(df_curr, left_on = 'country', right_on = 'Country', how = 'left')
merged_json_curr = json.loads(merged_curr.to_json())
json_data_curr = json.dumps(merged_json_curr)
geosource_curr = GeoJSONDataSource(geojson =  json_data_curr)


# boolinit = merged['Week']!=current_week
boolinit = merged['Week']==current_week
view = CDSView(source=geosource_all, filters=[BooleanFilter(boolinit)])
hover3 = HoverTool(tooltips = [('Country', '@Country'),('Sales','@SalesGain')])

#Define a sequential multi-hue color palette.
palette = brewer['YlGnBu'][8]
#Reverse color order so that dark blue is highest value
palette = palette[::-1]
#Instantiate LinearColorMapper that linearly maps numbers in a range, into a sequence of colors. Input nan_color.
color_mapper = LinearColorMapper(palette = palette, low = 0, high = 12, nan_color = '#d9d9d9')
#Define custom tick labels for color bar.
tick_labels = {'0': '0', '2':'2%',  '4':'4%',  '6':'6%', '8':'8%','10':'10%','12':'12%'}
#Create color bar. 
color_bar = ColorBar(color_mapper=color_mapper, label_standoff=6,width = 500, height = 20,
                     border_line_color=None,location = (0,0), orientation = 'horizontal', major_label_overrides = tick_labels)


#Create figure object.
p =  figure(title='Covid-19 Impact',  plot_width=900, plot_height=600, min_border=3,
            tools = [hover3,'box_zoom','wheel_zoom', 'pan','reset'],toolbar_location="above")

p.title.text_font_size = '20pt'
p.title.text_color = "darkblue"
p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None


#Add patch renderer to figure. 
p.patches('xs','ys', source = geosource_curr,fill_color = {'field' :'Color', 'transform' : color_mapper},
          line_color = 'black', line_width = 0.25, fill_alpha = 1)
p.add_layout(color_bar, 'below')


callback = CustomJS(args=dict(source=geosource_all, sc=geosource_curr), code="""       
        var f = slider.value;
        sc.data['Country'] = [];
        sc.data['Week'] = [];
        sc.data['SalesGain'] = [];
        for (var i = 0; i <= source.get_length(); i++){
          if ((source.data['Week'][i] == f ) || (source.data['Country'][i] == null) ){
            sc.data['SalesGain'].push(source.data['SalesGain'][i])
            sc.data['Week'].push(source.data['Week'][i])
            sc.data['Country'].push(source.data['Country'][i])
          }
        }
        sc.change.emit();
    """)
# select = Select(options=["201951","201952","201953"], title="Week", callback=callback)
# callback.args["select"] = select
# layout = column(select, p)

slider = Slider(start=1, end=5, value=current_week, step=1, title="Month", callback=callback)
callback.args["slider"] = slider
layout = column(slider, p)

#Display plot inline in Jupyter notebook
output_notebook()

show(layout)

Но в этом случае, как только я нажму на ползунок, всплывающая подсказка данных исчезнет sh. Входной файл карты мира можно найти здесь для плавного запуска кода: https://github.com/CrazyDaffodils/Interactive-Choropleth-Map-Using-Python/tree/master/bokeh-app/data

1 Ответ

0 голосов
/ 24 марта 2020

Всплывающая подсказка исчезает через небольшую задержку после того, как вы уберете мышь с глифа, который ее вызвал.

В настоящее время у Bokeh нет встроенного способа изменить это поведение. Существует открытая проблема для этого с обходным путем, который вы могли бы адаптировать к вашим потребностям: https://github.com/bokeh/bokeh/issues/5724

...