自定义JS回调,仅允许将一个圆圈显示在散景的“圆圈”图中

问题描述

我是bokeh的新手
我正在尝试使用bokeh复制https://www.worldometers.info/coronavirus/显示的线图。这是我的完整代码

from datetime import datetime,timedelta,date
import requests
import json

from bokeh.plotting import output_notebook,figure,show
from bokeh.models import ColumnDataSource,HoverTool,Title

    def DateFormatter(x):
        s = '01222020'
        global given_date
        given_date = datetime(month=int(s[:2]),day=int(s[2:4]),year=int(s[4:]))
        given_date += timedelta(days=x)
        final = str(given_date.strftime('%m{}%d{}%y').format('/','/'))
        if int(final[0:2])<10 and int(final[3:5])<10:
            final = final[1:]
            final = final[0 : 2 : ] + final[3 : :]
        elif int(final[0:2])<10:
            final = final[1:]
        elif int(final[3:5])<10:
            final = final[0 : 2 : ] + "/" + final[4 : :]
        return final

    def DateFormatterForPlot(x):
        s_plot = '01222020'
        global given_date_plot
        given_date_plot = datetime(month=int(s_plot[:2]),day=int(s_plot[2:4]),year=int(s_plot[4:]))
        given_date_plot += timedelta(days=x-1)
        final_plot1 = str(given_date_plot.strftime('%b %d'))
        if int(final_plot1[4:6])<10:
            final_plot1 = final_plot1[0 : 4 : ] + final_plot1[5 : :]
        return final_plot1

    finallist=[]
    l2 = []
    plot_list = []
    country = "India"
    chart_type = "cases"
    base_site = f'https://disease.sh/v3/covid-19/historical/{country}?lastdays=all'
    r = requests.get(base_site)
    if r.status_code == 200:
        packages_json = r.json()

        today_date = str(date.today().strftime('%m{}%d{}%y').format('/','/'))
        if int(today_date[0:2])<10 and int(today_date[3:5])<10:
            today_date = today_date[1:]
            today_date = today_date[0 : 2 : ] + today_date[3 : :]
        elif int(today_date[0:2])<10:
            today_date = today_date[1:]
        elif int(today_date[3:5])<10:
            today_date = today_date[0 : 2 : ] + "/" + today_date[4 : :]

        for i in range(1,1000):
            current_date = DateFormatter(i)
            if current_date==today_date:
                f_date = datetime(2020,1,22)
                delta = given_date - f_date
                break;

        for i in range(delta.days):#Day 157 is 26th June
            try:
                a = DateFormatter(i)
                packages_str = json.dumps(packages_json['timeline'][chart_type][a],indent=2)
                finallist.append(int(packages_str))
            except KeyError:
                pass

        count = 0
        for i in finallist:
            count = count + 1
        for i in range(1,count+1):
            l2.append(i)
        for i in l2:
            plot_list.append(DateFormatterForPlot(i))

    else:
        print("Not a valid country")

    source = ColumnDataSource(data=dict(y=finallist,x=l2,desc=plot_list))

    TOOLTIPS = """
    <style>
        .bk-tooltip>div:not(:first-child) {display:none;}
    </style>

    <b>X: </b> @desc <br>
    <b>Y: </b> @y{0,0}
    """

    plot = figure(background_fill_color='#fafafa',x_axis_label='Days',y_axis_label='Coronavirus {}'.format(chart_type.capitalize()),plot_width=1200,plot_height=400,toolbar_location=None)
    plot.line('x','y',source=source,legend = 'Number of {}'.format(chart_type.capitalize()),line_width=2,color='gray')
    cr = plot.circle('x',size=10,fill_color="grey",hover_fill_color="gainsboro",fill_alpha=0.1,line_color=None,hover_line_color="white",hover_fill_alpha=1)

    plot.add_tools(HoverTool(tooltips=TOOLTIPS,renderers=[cr]))
    plot.add_layout(Title(text="Coronavirus {} Example Graph".format(chart_type.capitalize()),align="center"),"above")
    plot.legend.location = 'top_left'
    plot.left[0].formatter.use_scientific = False #Used to disable scientific notation on y-axis

这里出现的问题是,当光标悬停在图形的密度较高的区域时,会显示多个圆形字形。

我认为Hovertool有一个CustomJS回调,只允许在鼠标悬停在其上方显示一个圆圈字形,但我无法实现它。

代码段的输出可以在此处查看http://geetanshjindal.pythonanywhere.com/charts/

我正在使用最新的bokeh版本2.1.1和python版本3.6

解决方法

我认为您有三种选择,所有这些都与降低密实度有关。

  • 减小圆形字形的大小,以使它们不重叠。 size=5对于您的绘图宽度似乎可以正常工作。
  • 增加绘图宽度,以使圆形字形更加隔开。当然,这将添加一个滚动条,因此可能不是最佳的用户体验。
  • 不要删除Bokeh工具栏,并鼓励用户与绘图进行交互-放大以查看所有圆形字形。

使用CSS选择性地显示工具提示的方法很有趣,但是它向用户隐藏了决策(“仅在重叠的两个圆圈的第一个圆圈显示工具提示”),可能引起混乱。

一种替代方法是完全删除工具提示,并在关键位置添加批注(传递一百万个案例?),然后将带有所有日期和值的表格放在侧面作为参考。

对于annotations,您可以结合使用Label和Bezier曲线模型:

from bokeh.models import Label,Bezier

test_label = Label(x=50,y=550000,x_units='data',y_units='data',text='Custom description of the datapoint',render_mode='css',border_line_color='white',border_line_alpha=1.0,border_line_width=3,background_fill_color='white',background_fill_alpha=1.0)

# play around with control point values for best effect
curve_source = ColumnDataSource(dict(
    x0=[150],y0=[395048],x1=[130],y1=[600000],cx0=[150],cy0=[500000],cx1=[150],cy1=[600000]       
    )
)

test_curve = Bezier(
    x0="x0",y0="y0",x1="x1",y1="y1",cx0="cx0",cy0="cy0",cx1="cx1",cy1="cy1",line_color="green",line_width=1)

plot.add_glyph(curve_source,test_curve)
plot.add_layout(test_label)

enter image description here