Bokeh日期时间坐标轴,控制次要刻度。

5
我有一个Web应用程序,用于提供Bokeh图表(使用Bokeh v0.12.7)。X轴为日期时间,显示用户定义的天数(日期范围默认为31天)。在不定义任何Bokeh刻度参数的情况下,主要刻度的数量默认为5,如以下实时Web应用程序中所示:

https://btac-web-plots.herokuapp.com/avyview?style=snowpacktracker

我希望能够在主刻度之间显示每天的次刻度。我尝试了以下方法,它允许任何日期范围长度,并指定每5天一个主刻度(startend是Pandas日期时间索引):
num_days = ((end - start) / np.timedelta64(1, 'D')).astype(int)
fig.xaxis.ticker = DaysTicker(
  days = np.arange(1, num_days, 5),
  num_minor_ticks = 4
  )

这个方法可以每5天正确显示主刻度,但是不会显示次刻度。另一个可能的解决方案是每天绘制主刻度,然后对于除了每5天之外的日期将刻度标签设置为不可见(不确定如何实现...)。
在这些图中显示每日次刻度的最佳方法是什么?
这是我的绘图代码的一部分(包括DaysTicker),以第一个面板为例:
fig = figure(
  title="New Snow, SWE, Snow Depth, and Settlement",
  name="newsnow_extra_fig",
  x_axis_type="datetime",
  y_axis_label='HN24 / SWE (in)',
  width=width, height=height2,
  tools=tools,
  toolbar_sticky=False,
  logo=None
  )
fig.y_range = Range1d(-12, 30)
fig.extra_y_ranges = {"Depth": Range1d(start=-72, end=180)}
fig.add_layout(LinearAxis(y_range_name="Depth", axis_label='HS (in)' ), 'right')
fig.yaxis.axis_label_standoff = 5

num_days = ((end - start) / np.timedelta64(1, 'D')).astype(int)
fig.xaxis.ticker = DaysTicker(
  days=np.arange(1,num_days,5),
  num_minor_ticks=4
  )

newcds, newcds_sums = create_newsnow_extra_cds(df, 'mid', start, end)

dline = fig.line(source=newcds,
  x='Date', y='depth', 
  line_color="blue", line_width=2, line_alpha=0.5, 
  legend="snow depth (HS)", name="depthline",
  y_range_name='Depth',
  )

nsbars = fig.vbar(source=newcds,
  x='newsnow_x', width=WIDTH, bottom=0, top='newsnow', 
  color="blue", alpha=0.9, legend='new snow (HN24)', name='nsbars'
  )
swebars = fig.vbar(source=newcds,
  x='swex10_x', width=WIDTH, bottom=0, top='swex10',
  color="blue", alpha=0.3, legend='SWE x 10', name='swebars'
  )

settlebars = fig.vbar(source=newcds, 
  x='Date', width=WIDTH*2.0, bottom='settle', top=0, 
  color="orange", alpha=0.75,
  name="settlebars", legend="settlement"
  )
2个回答

4
内置的 DatetimeAxis 并没有真正暴露出与您使用情况相对应的次要刻度配置。因此,您的选择是:
  • 创建一个自定义扩展来返回您想要的主要和次要刻度。

  • 对于每一天使用“day”刻度线,并隐藏您不想看到的刻度线,就像您建议的那样。

第二种方法(您的想法)可能是最简单的入门方式。这里有一个完整的代码示例,使用 FuncTickFormatter 生成“空白”刻度标签,除了每五天:
import numpy as np
import pandas as pd

from bokeh.io import output_file, show
from bokeh.models import DaysTicker, FuncTickFormatter
from bokeh.plotting import figure

x = pd.date_range("2017-12-01", "2017-12-31")
y = ([1,3,4]*11)[:31]

p = figure(plot_width=800, x_range=(x[0], x[-1]), x_axis_type="datetime")

p.line(x, y, line_dash="4 4", line_width=1, color='gray')

p.xaxis.ticker = DaysTicker(days=np.arange(1,32))
p.xaxis.major_label_orientation = 1.5

p.xaxis.formatter = FuncTickFormatter(code="""
    var date = new Date(tick)
    var day = date.getUTCDate(date)
    if ( day%5 == 0 ) { return day }
    else { return "" }
""")

output_file("foo.html")

show(p)

这将产生以下输出

enter image description here

如果您需要的不仅仅是日期数字,您可以调整JS代码中 FuncTickFormatter 的第一个if分支来执行更多操作而不仅仅是return day


谢谢,这很完美。我修改了JS代码,使其返回 monthday 以及格式化的字符串。问题是:因为我将 tick 参数传递给 new Date(),所以使用UTC (getUTCdate())是否必要?我假设 tick 参数由我的Pandas数据帧的索引控制? - pjw
当我没有使用它时,返回了我的本地时区的日期时间,这似乎不是很理想。但是根据情况,这可能或可能不合适。 - bigreddot
关于网格线的问题。也许这应该是一个新问题,但我正在尝试将网格线与标记的刻度线对齐。使用 fig.xgrid.ticker = DaysTicker(days=np.arange(0,num_days,5)),其中 num_days = 31,应该在 [ 0 5 10 15 20 25 30] 处放置刻度线。然而,网格似乎忽略了 30 并在 31 处放置了一条网格线....? - pjw
好的,有两件事情:首先,没有“0”日(即“12月0日”这样的东西),因此序列不应包括零(我不确定BokehJS会如何处理天数列表中的0,但没有意图)。其次,我认为可能存在某些逻辑,如果最后一个刻度靠近下个月的开始,则会被丢弃。这可能不是理想的,或者至少应该是可配置的。但是需要新的开发来更改,因此适当的下一步是使用详细信息的GitHub问题。 - bigreddot
与此同时,我能想到的唯一选择是提供一个自定义扩展股票代码。 - bigreddot

2

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