Python: Bokeh 悬停日期时间

17
我正在尝试在Python的Bokeh中制作一条线形图。我对Bokeh不熟悉,正在尝试在图表上应用悬停提示。该图的x轴具有时间戳值,这些值被转换为时代字符串。我已经查看了一些类似问题,并尝试使用解决方法来解决我的问题,但似乎没有起作用。在图表上,应显示时间的位置会出现???

对于我的代码,有什么建议吗?

时间戳格式为2016-12-29 02:49:12

另外,有人可以告诉我如何格式化x轴刻度以垂直显示吗?

p = figure(width=1100,height=300,tools='resize,pan,wheel_zoom,box_zoom,reset,previewsave,hover',logo=None)
p.title.text = "Time Series for Price in Euros"
p.grid.grid_line_alpha = 0
p.xaxis.axis_label = "Day"
p.yaxis.axis_label = "Euros"
p.ygrid.band_fill_color = "olive"
p.ygrid.band_fill_alpha = 0.1
p.circle(df['DateTime'],df['EuP'], size=4, legend='close',
  color='darkgrey', alpha=0.2)
p.xaxis.formatter = DatetimeTickFormatter(formats=dict(
hours=["%d %B %Y"],
days=["%d %B %Y"],
months=["%d %B %Y"],
years=["%d %B %Y"],
))

source = ColumnDataSource(data=dict(time=[x.strftime("%Y-%m-%d %H:%M:%S")for x in df['DateTime']]))
hover = p.select(dict(type=HoverTool))
hover.tooltips = {"time":'@time', "y":"$y"}
hover.mode = 'mouse'
p.line(df['DateTime'],df['EuP'],legend='Price',color='navy',alpha=0.7)
3个回答

32
自从这个答案最初发布以来,Bokeh已经进行了新的工作,使事情变得更简单。可以通过指定格式化程序,例如悬停工具,将日期时间字段直接格式化为日期时间。
HoverTool(tooltips=[('date', '@DateTime{%F}')],
          formatters={'@DateTime': 'datetime'})

不再需要在数据源中预先格式化日期字段,如下所示。有关更多信息,请参阅格式化工具提示字段

旧答案

你的工具提示问题在于你创建了一个带有日期字符串表示的源,但是p.line()调用不知道它。所以你必须传入一个包含工具提示、x和y值的列数据源。

这是你代码的一个可行变体:

from bokeh.plotting import figure, show
from bokeh.models.formatters import DatetimeTickFormatter
from bokeh.models import ColumnDataSource
from bokeh.models.tools import HoverTool
import pandas as pd
import numpy as np

data = {
    'DateTime' : pd.Series(
        ['2016-12-29 02:49:12',
        '2016-12-30 02:49:12',
        '2016-12-31 02:49:12'],
        dtype='datetime64[ns]'),
    'EuP' : [20,40,15]
}
df = pd.DataFrame(data)
df['tooltip'] = [x.strftime("%Y-%m-%d %H:%M:%S") for x in df['DateTime']]
p = figure(width=1100,height=300,tools='resize,pan,wheel_zoom,box_zoom,reset,previewsave,hover',logo=None)
p.title.text = "Time Series for Price in Euros"
p.grid.grid_line_alpha = 0
p.xaxis.axis_label = "Day"
p.yaxis.axis_label = "Euros"
p.ygrid.band_fill_color = "olive"
p.ygrid.band_fill_alpha = 0.1
p.circle(df['DateTime'],df['EuP'], size=4, legend='close',
  color='darkgrey', alpha=0.2)
p.xaxis.formatter = DatetimeTickFormatter(formats=dict(
 hours=["%d %B %Y"],
 days=["%d %B %Y"],
 months=["%d %B %Y"],
 years=["%d %B %Y"],
))
hover = p.select(dict(type=HoverTool))
tips = [('when','@tooltip'), ('y','$y')]
hover.tooltips = tips
hover.mode = 'mouse'
p.line(x='DateTime', y='EuP', source=ColumnDataSource(df),
       legend='Price',color='navy',alpha=0.7)
show(p)

还要注意的是,关于bokeh工具提示中缺乏格式选项的问题仍未解决。可能有一种更简单的方法来避免将日期字符串格式化为单独的列:

https://github.com/bokeh/bokeh/issues/1239

也有人能告诉我如何垂直显示x轴刻度吗?
在我看来,它们看起来很好,抱歉我无法帮助你。
希望这对你有所帮助!
附言:下次如果您发布一个带有导入语句和模拟数据框的工作脚本,以便进行测试,那将更好。整理这一切花了一些时间,但我正在学习Bokeh,所以没关系 :)

6
请使用“{%F %T}”来表示日期和时间。 - user2561747
谢谢 @user2561747! - Ignacio Franco
1
直到我在格式化程序的键前添加了@符号,例如@Datetime,它才对我起作用。 - racket99
1
这个格式是不是真的不太友好,还是只有我觉得? - Vincent Alex
根据 https://docs.bokeh.org/en/latest/docs/user_guide/tools.html 文档,我尝试了一行 hover_tool.formatters = { "$x": "datetime"} 代码来处理特殊的 $x 变量,但是失败了,最终我只能回到类似于 HoverTool(tooltips=[('date', '@DateTime{%F %T}')], formatters={'@DateTime': 'datetime'}) 的方案。哦,HoverTool(tooltips=[('date', '$x{%F %T}')], formatters={'$x': 'datetime'}) 也可以。 - Dave X
显示剩余3条评论

15

很抱歉我没有足够的声望来评论。

@Alex提供的答案对我不起作用(Bokeh 2.0.1),因为它在格式化程序中缺少一个简单的@符号。正常的代码是这样的:

HoverTool(tooltips=[('date', '@DateTime{%F}')],
          formatters={'@DateTime': 'datetime'})

1
感谢@don-fruendo和racket99!我最近没有使用Bokeh,所以我认为你们可能是正确的。 - Alex G Rice
@LeeviL 不确定你在问什么:对我的原始答案的更正(更新)已经被bigreddot简洁地进行了。 - Alex G Rice
在您接受的答案中,@AlexGRice提到了formatters={'DateTime': 'datetime'},但实际上应该是formatters={'@DateTime': 'datetime'} - Leevi L

0
我已经为bokeh中的ScatterPlot创建了一个包装器。
class Visualization():
    WIDTH = 1000
    TOOLS = "pan,wheel_zoom,box_zoom,reset,save"
 
class ScatterChart(Visualization):
    def __init__(self, data, spec:Dict):
        self.data = data
        self.x_column = spec["x_axis"]
        self.y_column = spec["y_axis"]    
        self.series_ = spec["series_column"]
        
        self.xlabel = spec['xlabel'] 
        self.ylabel = spec['ylabel'] 
        self.title = spec['title'] 
        
    def prepare_data(self):
        
        # Get  Axis Type
        self.xtype = 'datetime' if self.data.dtypes[self.x_column].type is np.datetime64 else 'linear'
        self.ytype = 'datetime' if self.data.dtypes[self.x_column].type is np.datetime64 else 'linear'
        
        return self.data
    
    def render(self):
        df_ = self.prepare_data()
        
        format_= {}
        tool_tip=[]
        
        # For axis
        for col in [self.x_column, self.y_column , self.series_]:
            
            if self.data.dtypes[col].type is np.datetime64:
                format_['@' + str(col) ] = "datetime" # formatter
                tool_tip.append(tuple([str(col) , '@' + str(col) + '{%F}'])) # tool-tip
                
            else:
                format_['@' + str(col) ] = "numeral" # 
                tool_tip.append(tuple([str(col) , '@' + str(col)]))
            
        
        # print(format_)
        # print(tool_tip)
        
        
        # Add Hover parameters
        hover = HoverTool(tooltips= tool_tip
            , formatters=format_   )
        
        
        
        p=figure(
                 width = super().WIDTH,
                 height = 500,
                 x_axis_label = self.xlabel,
                 x_axis_type=self.xtype,
                 y_axis_label = self.ylabel,
                 y_axis_type=self.ytype,
                 title = self.title,
                 tools = super().TOOLS
                 )
        
        # Get Only Top 10 groups/series to display
        for value, color in zip(islice(self.data.groupby(by=[self.series_]
                            )[self.series_].count().sort_values(ascending=False).rename('cnt').reset_index()[self.series_].tolist(), 10), Category10[10]):
            
            p.scatter(
                    x=self.x_column,
                    y=self.y_column,
                    source=df_.loc[(df_[self.series_]==value)],
                    color=color,
                    legend_group=self.series_
                      )
            
        p.add_tools(hover)
        p.toolbar.logo = None
        p.legend.location = "top_left"
        p.legend.click_policy="hide"
                
        return p
    
# end of ScatterChart

这是我初始化的方式

from visualization import ScatterChart
sc = ScatterChart( 
    df,
    {'x_axis' :'ts', 
     'y_axis': 'Discus',
     'series_column': 'Competition',
      'xlabel':'Discus',
      'ylabel':'Javeline',
     'title':'Discus Vs Javeline'
     })
d = sc.render()
show(d)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接