【问题标题】:Bokeh datetime axis, control of minor ticks散景日期时间轴,小刻度的控制
【发布时间】:2018-06-10 18:59:43
【问题描述】:

我有一个提供散景图的网络应用程序(使用散景 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"
  )

【问题讨论】:

    标签: python datetime bokeh


    【解决方案1】:

    内置的DatetimeAxis 并没有真正公开与您的用例相对应的次要刻度配置。所以你的选择是:

    • 创建一个custom extension 以准确返回您想要的主要和次要刻度

    • 按照您的建议,为每一天使用“日”标记并隐藏您不想看到的标记。

    第二条路径(您的想法)可能是最容易上手的。这是一个完整的代码示例,它使用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)
    

    生成这个输出

    如果您想要的不仅仅是日期数,您可以调整 JS 代码中的第一个 if 分支,以使 FuncTickFormatter 不仅仅是 return day

    【讨论】:

    • 谢谢这是完美的。我修改了 JS 以返回 monthday 以及一个格式化的字符串。问题:因为我将tick 传递给new Date(),是否有必要使用UTC(getUTCdate())?我假设 tick 参数由我的 Pandas 数据框的索引控制?
    • 当我尝试不使用它时,返回了我当地时区的日期时间,这似乎并不理想。但这可能适合也可能不适合,具体取决于您的情况。
    • 另外,关于网格线。也许这应该是一个新问题,但我正在尝试将网格线与标记的刻度线对齐。使用fig.xgrid.ticker = DaysTicker(days=np.arange(0,num_days,5)),其中num_days = 31,应将刻度放在[ 0 5 10 15 20 25 30]。但是,网格似乎忽略了 30 并在 31 处放置了一条网格线......?
    • 嗯,有两件事:首先,没有“0”天(即没有“12 月 0”之类的东西)所以序列不应该包括零(我不太确定副手BokehJS 将在天数列表中使用 0 做什么,但没有任何意图)。其次,我认为可能有一些逻辑导致最后一个刻度在接近下月初时被丢弃。这可能并不理想,或者至少应该是可配置的。但需要新的开发来改变,因此适当的下一步将是一个包含详细信息的 GitHub 问题。
    • 与此同时,我能想到的唯一选择是自定义扩展代码。
    【解决方案2】:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-24
      • 1970-01-01
      • 2018-02-22
      • 1970-01-01
      • 2014-08-25
      • 1970-01-01
      • 2016-11-03
      相关资源
      最近更新 更多