- For an
Arrow
, x_start
and x_end
must be a datetime
format, not a string
or a dataframe
.
x_start=pd.to_datetime('2010-10-09')
- The coordinates for the arrow may not be passed as a dataframe, they must be passed as individual values, which is done in a loop below.
x_
is the date from the datetime index.
y_
is the y intersection point, to which an offset (e.g. +5
) may be added
- This example was used, and arrows were added to it
- See Labels for text annotations
import pandas as pd
from bokeh.models import Arrow, NormalHead, OpenHead, VeeHead, Label
from bokeh.plotting import figure, show
from bokeh.sampledata.glucose import data
from bokeh.io import output_notebook, curdoc # output_file
output_notebook()
# for a file, uncomment the next line and output_file in the imports
# output_file("box_annotation.html", title="box_annotation.py example")
TOOLS = "pan,wheel_zoom,box_zoom,reset,save"
#reduce data size
data = data.loc['2010-10-06':'2010-10-13'].copy()
# test line to show where glucose and line cross each other
data['line'] = 170
# determine where the lines cross
match = data[data.glucose == data.line]
p = figure(x_axis_type="datetime", tools=TOOLS)
p.line(data.index.to_series(), data['glucose'], line_color="gray", line_width=1, legend_label="glucose")
p.line(data.index.to_series(), data['line'], line_color="purple", line_width=1, legend_label="line")
# add arrows to all spots where the lines are equal
for x_, (y_, _) in match[['glucose', 'line']].iterrows():
p.add_layout(Arrow(end=VeeHead(line_color="blue", line_width=4, fill_color='blue'),
line_color='blue', line_width=4,
x_start=x_, y_start= y_ + 130,
x_end=x_, y_end=y_ + 5))
p.title.text = "Glucose Range"
p.xgrid[0].grid_line_color=None
p.ygrid[0].grid_line_alpha=0.5
p.xaxis.axis_label = 'Time'
p.yaxis.axis_label = 'Value'
show(p)
Update
- In the following section:
x_start=df['Date']
& x_end=df['Date']
are used instead of x_
, which should be a single date value, not a Series
of dates.
- The
for-loop
selects the incorrect values to be x_
and y_
. In my original match
, the dates are in the index, but your match
has dates in a column.
match = df[((df.short_ema.shift(1) > df.long_ema.shift(1)) & (df.short_ema.shift(2)< df.long_ema.shift(2)))]
for x_, (y_, _) in match[['Date', 'long_ema']].iterrows():
print(x_,y_)
p.add_layout(Arrow(end=VeeHead(line_color="blue", line_width=4, fill_color='blue'),
line_color='blue', line_width=4,
x_start=df['Date'], y_start= y_ + 3,
x_end=df['Date'], y_end=y_ + 1))
Corrected Code
for _, (x_, y_) in match[['Date', 'long_ema']].iterrows():
print(x_,y_)
p.add_layout(Arrow(end=VeeHead(line_color="blue", line_width=4, fill_color='blue'),
line_color='blue', line_width=4,
x_start=x_, y_start= y_ + 3,
x_end=x_, y_end=y_ + 1))
show(p)