2021年3月20日星期六

Bokeh hovertool display too crowded for multiple chart lines

Recently I have one requirement to visualize some collected data with chart plot, we want users to inspect the exact data when mouse/cursor hover over the chart line.

I understand we can do this via Bokeh HoverTool. One issue we faced now is when there are multiple lines especially when they are close to each other, hover tool showing as labels would make this even more crowded. Thus I was wondering if we can show these hover tips in the legend area and update the corresponding legend data when mouse hover over different line.

I have spent a lot of time on this topic in recent weeks and come up with following two solutions but still not perfect, that's why I was wondering if we can show these hover tips in legend label, screenshot and sample code is attached as follows, thanks!

Solution 1 was we can now set hover.mode to vline, so we can exactly nominate different renders for different lines, but this would make as many labels as lines.

enter image description here

#! /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)  

Solution 2 was set hover.mode to mouse and use HoverTool in general which means we didn't nominate renders for exact line. However, in this case, we have to touch the line, if still keep hover.mode as vline, there would be as many copies as line numbers.

enter image description here

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)  
https://stackoverflow.com/questions/66722437/bokeh-hovertool-display-too-crowded-for-multiple-chart-lines March 20, 2021 at 09:57PM

没有评论:

发表评论