Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bokeh datetime axis, control of minor ticks

I have a web app that serves Bokeh plots (uses Bokeh v0.12.7). The x-axis is datetime, displaying a user-defined number of days (the date range defaults to 31 days). Without defining any tick parameters in Bokeh, the number of major ticks defaults to 5, as shown on the live web app here:

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

I want to display minor ticks for every day between the major ticks. I tried the following, which allows for any date range length, and specifies a major tick every 5th day (start and end are Pandas Datetime indices):

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
  )

This correctly displays major ticks every 5th day, but minor ticks are not displayed. Another potential solution might be to plot major ticks every day, and then set tick labels to invisible for days except every 5th day (unsure how to implement this...).

What is the best method to display daily minor ticks on these plots?

Here is a section of my plotting code (including DaysTicker), using the first panel as an example:

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"
  )
like image 705
PJW Avatar asked Dec 30 '17 20:12

PJW


1 Answers

The built in DatetimeAxis does not really expose a minor tick configuration corresponding to your use case. So your options would be:

  • create a custom extension to return exactly the major and minor ticks you want

  • Use a "day" ticker for every day and hide ticks you don't want to see, as you suggested.

The second path (your idea) is probably the simplest to get started. Here is a complete code sample that uses FuncTickFormatter to generate "blank" tick labels except every fifth day:

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)

That generates this output

enter image description here

If you want more than just the day number, you would tweak the first if branch in the JS code for the FuncTickFormatter to do more than just return day

like image 174
bigreddot Avatar answered Nov 14 '22 23:11

bigreddot