Timeseries plots#
Units#
Bokeh can automatically handle many kinds of datetime types, for instance Numpy datetime arrays and Pandas datetime series, as well as Python built-in datetime types. It can sometimes be helpful to understand how Bokeh represents these values.
Internally, all datetime values are floating point values that represent
milliseconds-since-epoch (specifically, “epoch” here refers to unix time,
i.e. 1 January 1970 00:00:00 UTC). Bokeh will convert datetime values to this
floating point format before passing on to BokehJS. On occasion (e.g. in
CustomJS
callbacks) it may be necessary to use these values directly.
Range tool#
It is often desirable to be able to zoom in on one region of a timeseries
while still seeing a view of the full series for context. The RangeTool
can be used to interactively define a region in one plot that results in a
zoomed view on another plot. This is demonstrated below:
import numpy as np
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, RangeTool
from bokeh.plotting import figure, show
from bokeh.sampledata.stocks import AAPL
dates = np.array(AAPL['date'], dtype=np.datetime64)
source = ColumnDataSource(data=dict(date=dates, close=AAPL['adj_close']))
p = figure(height=300, width=800, tools="xpan", toolbar_location=None,
x_axis_type="datetime", x_axis_location="above",
background_fill_color="#efefef", x_range=(dates[1500], dates[2500]))
p.line('date', 'close', source=source)
p.yaxis.axis_label = 'Price'
select = figure(title="Drag the middle and edges of the selection box to change the range above",
height=130, width=800, y_range=p.y_range,
x_axis_type="datetime", y_axis_type=None,
tools="", toolbar_location=None, background_fill_color="#efefef")
range_tool = RangeTool(x_range=p.x_range, start_gesture="pan")
range_tool.overlay.fill_color = "navy"
range_tool.overlay.fill_alpha = 0.2
select.line('date', 'close', source=source)
select.ygrid.grid_line_color = None
select.add_tools(range_tool)
show(column(p, select))
Candlestick chart#
import pandas as pd
from bokeh.models import BoxAnnotation
from bokeh.plotting import figure, show
from bokeh.sampledata.stocks import MSFT
df = pd.DataFrame(MSFT)[60:120]
df["date"] = pd.to_datetime(df["date"])
inc = df.close > df.open
dec = df.open > df.close
non_working_days = df[['date']].assign(diff=df['date'].diff()-pd.Timedelta('1D'))
non_working_days = non_working_days[non_working_days['diff']>=pd.Timedelta('1D')]
df['date'] += pd.Timedelta('12h') # move candles to the center of the day
TOOLS = "pan,wheel_zoom,box_zoom,reset,save"
p = figure(x_axis_type="datetime", tools=TOOLS, width=1000, height=400,
title="MSFT Candlestick", background_fill_color="#efefef")
p.xaxis.major_label_orientation = 0.8 # radians
boxes = [
BoxAnnotation(fill_color="#bbbbbb", fill_alpha=0.2, left=date-diff, right=date)
for date, diff in non_working_days.values
]
p.renderers.extend(boxes)
p.segment(df.date, df.high, df.date, df.low, color="black")
p.vbar(df.date[dec], pd.Timedelta('16h'), df.open[dec], df.close[dec], color="#eb3c40")
p.vbar(df.date[inc], pd.Timedelta('16h'), df.open[inc], df.close[inc], fill_color="white",
line_color="#49a3a3", line_width=2)
show(p)
Missing dates#
import pandas as pd
from bokeh.plotting import figure, show
from bokeh.sampledata.stocks import MSFT
df = pd.DataFrame(MSFT)[60:120]
df["date"] = pd.to_datetime(df["date"])
inc = df.close > df.open
dec = df.open > df.close
TOOLS = "pan,wheel_zoom,box_zoom,reset,save"
p = figure(tools=TOOLS, width=1000, height=400,
title="MSFT Candlestick without missing dates",
background_fill_color="#efefef")
p.xaxis.major_label_orientation = 0.8 # radians
p.x_range.range_padding = 0.05
# map dataframe indices to date strings and use as label overrides
p.xaxis.major_label_overrides = {
i: date.strftime('%b %d') for i, date in zip(df.index, df["date"])
}
# one tick per week (5 weekdays)
p.xaxis.ticker = list(range(df.index[0], df.index[-1], 5))
p.segment(df.index, df.high, df.index, df.low, color="black")
p.vbar(df.index[dec], 0.6, df.open[dec], df.close[dec], color="#eb3c40")
p.vbar(df.index[inc], 0.6, df.open[inc], df.close[inc], fill_color="white",
line_color="#49a3a3", line_width=2)
show(p)