首页 > 解决方案 > 散景悬停工具显示过于拥挤,无法显示多条图表线

问题描述

最近我有一个需求,用图表来可视化一些收集的数据,我们希望用户在鼠标/光标悬停在图表线上时检查确切的数据。

我知道我们可以通过 Bokeh 做到这一点HoverTool。我们现在面临的一个问题是当有多条线时,尤其是当它们彼此靠近时,显示为标签的悬停工具会使它更加拥挤。因此我想知道我们是否可以在图例区域显示这些悬停提示,并在鼠标悬停在不同的行上时更新相应的图例数据。

最近几周我在这个话题上花了很多时间,并提出了以下两个解决方案,但仍然不完美,这就是为什么我想知道我们是否可以在图例标签中显示这些悬停提示,截图和示例代码如下所示,谢谢!

解决方案 1是我们现在可以将 hover.mode 设置为vline,因此我们可以准确地为不同的线条指定不同的渲染,但这会使标签与线条一样多。

在此处输入图像描述

#! /usr/bin/env python

import numpy as np
import pandas as pd
from datetime import datetime
import time
from bokeh.io import output_file, show, save
from bokeh.plotting import figure
from bokeh.plotting import ColumnDataSource
from bokeh.layouts import gridplot
from bokeh.models import LinearAxis, Range1d
from bokeh.models.widgets import Tabs, Panel
from bokeh.models import HoverTool
from bokeh.models import CrosshairTool


def get_data():

    df = pd.DataFrame(np.array([['08:00:00', 11, 13, 15, 17], ['08:30:00', 13, 15, 17, 19],
                                ['09:00:00', 11, 13, 15, 17], ['09:30:00', 13, 15, 17, 19],
                                ['10:00:00', 11, 13, 15, 17], ['10:30:00', 13, 15, 17, 19],
                                ['14:00:00', 11, 13, 15, 17], ['14:30:00', 13, 15, 17, 19],
                                ['15:00:00', 11, 13, 15, 17], ['15:30:00', 13, 15, 17, 19],
                                ['16:00:00', 11, 13, 15, 17], ['16:30:00', 13, 15, 17, 19],
                                ['19:00:00', 11, 13, 15, 17], ['19:30:00', 13, 15, 17, 19],
                                ['20:00:00', 11, 13, 15, 17], ['20:30:00', 13, 15, 17, 19],
                                ['21:00:00', 11, 13, 15, 17], ['21:30:00', 13, 15, 17, 19],
                                ['22:00:00', 11, 13, 15, 17], ['22:30:00', 13, 15, 17, 19]]),
                      columns=['time', 'red', 'white', 'blue', 'yellow'])

    column_data_source = ColumnDataSource(data={
        'x': pd.to_datetime(df['time'], format='%H:%M:%S'),
        'x0': pd.Series([x.strftime('%H:%M') for x in pd.to_datetime(df['time'], format='%H:%M:%S')]),
        'y_red': df['red'],
        'y_white': df['white'],
        'y_blue': df['blue'],
        'y_yellow': df['yellow']
    })

    return column_data_source


def plot_figure(cds):

    plot = figure(plot_width=1200, plot_height=600,
                  x_axis_type='datetime',
                  y_range=(10, 20))

    cross = CrosshairTool()
    cross.line_color = 'white'
    cross.line_alpha = 0.7
    plot.add_tools(cross)

    plot.title.text = 'Number of Cars Collected at Different Time'
    plot.background_fill_color = 'black'
    plot.xgrid.grid_line_color = None
    plot.ygrid.grid_line_color = None
    plot.xaxis.axis_label = 'time'
    
    line_red = plot.line(x='x', y='y_red', source=cds, color='red', legend_label='red')
    line_white = plot.line(x='x', y='y_white', source=cds, color='white', legend_label='white')
    line_black = plot.line(x='x', y='y_blue', source=cds, color='blue', legend_label='blue')
    line_yellow = plot.line(x='x', y='y_yellow', source=cds, color='yellow', legend_label='yellow')

    plot.add_tools(HoverTool(renderers=[line_red], tooltips=[('time', '@x0'), ('number', '@y_red')], mode='vline'))
    plot.add_tools(HoverTool(renderers=[line_white], tooltips=[('time', '@x0'), ('number', '@y_white')], mode='vline'))
    plot.add_tools(HoverTool(renderers=[line_black], tooltips=[('time', '@x0'), ('number', '@y_blue')], mode='vline'))
    plot.add_tools(HoverTool(renderers=[line_yellow], tooltips=[('time', '@x0'), ('number', '@y_yellow')], mode='vline'))
    
    plot.legend.location = 'bottom_left'
    # plot.legend.orientation = 'horizontal'
    plot.legend.label_text_color = 'white'
    plot.legend.background_fill_color = 'black'
    plot.legend.background_fill_alpha = 0.1

    show(plot)


if __name__ == "__main__":

    cds = get_data()
    plot_figure(cds)

解决方案 2设置为 hover.modemouseHoverTool在一般情况下使用,这意味着我们没有为精确线指定渲染。但是,在这种情况下,我们必须触摸行,如果仍然保持 hover.mode 为vline,那么副本将与行号一样多。

在此处输入图像描述

def plot_figure(cds):

    hover = HoverTool(tooltips=[
        ("time", "@x0"),
        ('red', '@y_red'),
        ('white', '@y_white'),
        ('blue', '@y_blue'),
        ('yellow', '@y_yellow'),
    ])
    hover.mode = 'mouse'
    # hover.mode = 'vline'

    plot = figure(plot_width=1200, plot_height=600,
                  tools=[hover],
                  x_axis_type='datetime',
                  y_range=(10, 20))

    cross = CrosshairTool()
    cross.line_color = 'white'
    cross.line_alpha = 0.7
    plot.add_tools(cross)

    plot.title.text = 'Number of Cars Collected at Different Time'
    plot.background_fill_color = 'black'
    plot.xgrid.grid_line_color = None
    plot.ygrid.grid_line_color = None
    plot.xaxis.axis_label = 'time'
    
    line_red = plot.line(x='x', y='y_red', source=cds, color='red', legend_label='red')
    line_white = plot.line(x='x', y='y_white', source=cds, color='white', legend_label='white')
    line_black = plot.line(x='x', y='y_blue', source=cds, color='blue', legend_label='blue')
    line_yellow = plot.line(x='x', y='y_yellow', source=cds, color='yellow', legend_label='yellow')
    
    plot.legend.location = 'bottom_left'
    # plot.legend.orientation = 'horizontal'
    plot.legend.label_text_color = 'white'
    plot.legend.background_fill_color = 'black'
    plot.legend.background_fill_alpha = 0.1

    show(plot)

标签: pythonbokeh

解决方案


推荐阅读