Python:散景悬停日期时间

Python: Bokeh hover date time

我正在尝试通过 Python 中的 Bokeh 获取线图。我是 Bokeh 的新手,我正在尝试在绘图上应用悬停工具提示。该图的 x 轴具有时间戳值,这些值被转换为纪元字符串。我在这里审查了一些相同的问题,并尝试对我的案例使用解决方法,但它似乎不起作用。在情节上它给出了 ??? 时间应该出现的地方。

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

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

也有人可以告诉我如何格式化 x-axis 刻度以垂直显示吗?

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)

自从最初发布此答案后,Bokeh 已投入新工作以简化操作。日期时间字段可以通过悬停工具直接格式化为日期时间,方法是指定格式化程序,例如:

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

不再需要像下面这样预先格式化数据源中的日期字段。有关详细信息,请参阅 Formatting Tooltip Fields


旧答案

你的工具提示的问题是你创建了一个带有日期字符串表示的源,但是 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)

另请注意,散景工具提示中缺少格式化选项是一个悬而未决的问题。可能有更简单的方法不必将日期字符串格式化为单独的列:

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

Also can someone tell how do I format x-axis ticks to show up vertically ?

它们对我来说很好,很抱歉,我不能帮助它。

希望对您有所帮助!

PS 如果您发布一个带有导入语句的工作脚本和一个模拟数据框以便于测试,下次会更好。花了一些时间来整理这一切。但我正在学习散景,所以这很好:)

抱歉没有发表评论,我没有足够的声誉。

@Alex 接受的答案对我不起作用 (Bokeh 2.0.1),因为它在格式化程序中缺少一个简单的 @-sign。工作代码是这样的:

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

我已经为散景中的散点图创建了一个包装器。

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)