how to get the axis values on the HoverTool - Bokeh - python

I have written a code that reads from a .csv files and converts it into a pandas dataframe. I then proceed to plot the chart with candlesticks. The chart itself is good but when i try to use the HoverTools i cannot seem to add the axis values to the tool.
I used column data source but i wasnt able to understand it.
import pandas as pd
from math import pi
from bokeh.plotting import figure, show, output_file
from bokeh.models import HoverTool
df = pd.read_csv('/Users/robindhillon/Desktop/pyfiles/EURUSD.csv')
df.columns = ['date','open','high','low','close','volume']
df['date'] = pd.to_datetime([x[:-9] for x in
df['date'].squeeze().tolist()], dayfirst=True)
inc = df.close > df.open
dec = df.open > df.close
w = 86400000
hover = HoverTool(
tooltips=[
('date', '#date'),
('open', '#open' ),
('high', '#high' ),
('low', '#low' ),
('close', '#close'),
],
formatters={
'date' : 'datetime',
'open' : 'numeral',
'high' : 'numeral',
'low' : 'numeral',
'close': 'numeral',
},
mode='vline'
)
TOOLS = 'pan,wheel_zoom,box_zoom,reset,save,crosshair'
p = figure(x_axis_type = 'datetime', tools=TOOLS, plot_width=1200,
title='test')
p.xaxis.major_label_orientation = pi/4
p.grid.grid_line_alpha = 0.3
p.add_tools(hover)
p.segment(df.date, df.high, df.date, df.low, color="black")
p.vbar(df.date[inc], w, df.open[inc], df.close[inc],
fill_color="#12C98C", line_color="black")
p.vbar(df.date[dec], w, df.open[dec], df.close[dec],
fill_color="#F2583E", line_color="black")
output_file("candlestick.html", title="candlestick.py example")
show(p) # open a browser

Field names that begin with # are associated with columns in a ColumnDataSource. For instance the field name "#date" will display values from the "date" column whenever a hover is triggered. If the hover is for the 17th glyph, then the hover tooltip will correspondingly display the 17th date value. The hover tool won't work if you use field names that begin with # without a ColumnDataSource
import pandas as pd
from math import pi
from bokeh.plotting import figure, show, output_file
from bokeh.models import HoverTool, ColumnDataSource
df = pd.read_csv('/Users/robindhillon/Desktop/pyfiles/EURUSD.csv')
df.columns = ['date','open','high','low','close','volume']
df['date'] = pd.to_datetime([x[:-9] for x in
df['date'].squeeze().tolist()], dayfirst=True)
inc = df.close > df.open
dec = df.open > df.close
w = 86400000
hover = HoverTool(
tooltips=[
('date', '#date{%F}'),
('open', '#open' ),
('high', '#high' ),
('low', '#low' ),
('close', '#close'),
],
formatters={
'date' : 'datetime',
'open' : 'numeral',
'high' : 'numeral',
'low' : 'numeral',
'close': 'numeral',
},
mode='vline'
)
df['dateinc'] = df.date[inc]
df['openinc'] = df.open[inc]
df['closeinc'] = df.close[inc]
df['datedec'] = df.date[dec]
df['opendec'] = df.open[dec]
df['closedec'] = df.close[dec]
source = ColumnDataSource(df)
TOOLS = 'pan,wheel_zoom,box_zoom,reset,save,crosshair'
p = figure(x_axis_type = 'datetime', tools=TOOLS, plot_width=1200,
title='test')
p.xaxis.major_label_orientation = pi/4
p.grid.grid_line_alpha = 0.3
p.add_tools(hover)
p.segment('date', 'high', 'date', 'low', color="black", source=source)
p.vbar('dateinc', w, 'openinc', 'closeinc', fill_color="#12C98C", line_color="black", source=source)
p.vbar('datedec', w, 'opendec', 'closedec', fill_color="#F2583E", line_color="black", source=source)
output_file("candlestick.html", title="candlestick.py example")
show(p) # open a browser

In addition to the #field_name hover tool specification, there are also some "special variables" to display specific information not-related to the data source. To display the data-space value of the x-coordinate under the cursor, use $x

Related

How to get Bokeh hovertool working for candlesticks chart?

The goal was to have my mouse hover over the candlesticks and have the data appear while I do so. I was able to get the Bokeh hover tool working but, I am not able to pull and display the data. I was able to get the (x,y) data to appear but, not the rest. This is what I have so far:
#Importing Libraries
import requests
import pandas as pd
import datetime
from bokeh.plotting import ColumnDataSource, figure, output_file, show
from math import pi
#Request ticker symbol
API_KEY = 'YOUR API KEY'
symbol = input('Enter ticker symbol: ')
r = requests.get('https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=' + symbol + '&apikey=' + API_KEY)
print(r.status_code)
result = r.json()
dataForAllDays = result['Time Series (Daily)']
#convert to dataframe
df = pd.DataFrame.from_dict(dataForAllDays, orient='index')
df = df.reset_index()
#rename columns
df = df.rename(index=str, columns={"index": "date", "1. open": "open", "2. high": "high", "3. low": "low", "4. close": "close","5. volume":"volume"})
#Changing to datetime
df['date'] = pd.to_datetime(df['date'])
#Sort according to date
df = df.sort_values(by=['date'])
#Changing the datatype
df.open = df.open.astype(float)
df.close = df.close.astype(float)
df.high = df.high.astype(float)
df.low = df.low.astype(float)
df.volume = df.volume.astype(int)
#check the data
df.head()
#Hovertool Tooltips
source = ColumnDataSource(data=dict(date=df['date'], open=df.open, close=df.close))
TOOLTIPS = [
("(x,y)", "($x, $y)"),
("date", "#date"),
("open", "df.open"),
("open", "#open"),
("close", "df.close"),
("close", "#close"),
("percent","#changepercent"),
]
#Check the datatype
df.info()
inc = df.close > df.open
dec = df.open > df.close
w = 12*60*60*1000 # half day in ms
TOOLS = "pan,wheel_zoom,box_zoom,reset,save"
title = symbol + ' Chart'
p = figure(x_axis_type="datetime", tools=TOOLS, tooltips=TOOLTIPS, plot_width=1000, title = title)
p.xaxis.major_label_orientation = pi/4
p.grid.grid_line_alpha=0.3
p.segment(df.date, df.high, df.date, df.low, color="black")
p.vbar(df.date[inc], w, df.open[inc], df.close[inc], fill_color="#a30e15", line_color="black")
p.vbar(df.date[dec], w, df.open[dec], df.close[dec], fill_color="#53680c", line_color="black")
#Store as a HTML file
output_file("stock_information.html", title="candlestick.py")
# Display in browser
show(p)
Here is a full example of functioning one minute candlestick with hover tooltips and bbands thrown in.
source = ColumnDataSource(data=df)
hover = HoverTool(
tooltips=[
('d', '#timestamp{%H:%M}'),
('o', '#open{0}'),
('h', '#high{0}'),
('l', '#low{0}'),
('c', '#close{0}'),
('v', '#volume{0}'),
],
formatters={
'#timestamp': 'datetime'
},
mode='mouse'
)
inc_b = source.data['close'] > source.data['open']
inc = CDSView(source=source, filters=[BooleanFilter(inc_b)])
dec_b = source.data['open'] > source.data['close']
dec = CDSView(source=source, filters=[BooleanFilter(dec_b)])
w = 60000
p = figure(x_axis_type="datetime", sizing_mode="stretch_width", height=400)
p.segment(source=source, x0='timestamp', x1='timestamp', y0='high', y1='low', color="black")
p.vbar(source=source, view=inc, x='timestamp', width=w, top='open', bottom='close', fill_color="green", line_color="green")
p.vbar(source=source, view=dec, x='timestamp', width=w, top='open', bottom='close', fill_color="red", line_color="red")
p.line(source=source, x='timestamp', y='bband_up', line_width=1, alpha=0.8, legend_label='bband_up', color='green')
p.line(source=source, x='timestamp', y='bband_mid', line_width=1, alpha=0.8, legend_label='bband_mid', color='blue')
p.line(source=source, x='timestamp', y='bband_low', line_width=1, alpha=0.8, legend_label='bband_low', color='red')
p.add_tools(hover)
p.legend.location = "top_left"
p.legend.click_policy = "hide"
show(p)
The only values that are meaningful in the tooltip specification, are:
pre-defined "special variables" denoted by starting with $
references to columns in the ColumnDataSource, denoted by starting with #
So things like "df.open" are meaningless in this context. The actual code to make the hovertool executes in the browser, in javascript, where Python and Pandas and your DataFrame df do not exist. If you want data to show up in a HoverTool then you will have to put a column in your CDS with that data, and reference it in the hover tool specification using the #column_name syntax.
You may refer to the documentation for more information:
https://docs.bokeh.org/en/latest/docs/user_guide/tools.html#hovertool

How to toggle between figures using a dropdown menu in Bokeh?

I have a figure where I already added buttons to display different plots. How can I add another couple figures, each having different sets of plots and switch between the figures using a dropdown menu? I'm trying to condense the code as much as possible, as to not be rewriting the same functions for each figure. What is the best way to go about that? In the sample code below I didn't include a slider and three buttons for the second figure, but I would like all figures to have them
import numpy as np
import pandas as pd
import warnings
from bokeh.layouts import widgetbox
from bokeh.plotting import figure, show, output_file, output_notebook
from bokeh.palettes import Spectral11, colorblind, Inferno, BuGn, brewer
from bokeh.models import HoverTool, value, LabelSet, Legend, ColumnDataSource, LinearColorMapper, BasicTicker, PrintfTickFormatter, ColorBar
from bokeh.models.widgets import DateRangeSlider, CheckboxButtonGroup
from bokeh.models import CustomJS, ColumnDataSource
from bokeh.layouts import column, row
from json import loads
import ast
import datetime as dt
warnings.filterwarnings('ignore')
TOOLS = 'save,pan,box_zoom,reset,wheel_zoom'
p = figure(title="data", plot_height=400, tools=TOOLS, plot_width=1300)
start_date = dt.datetime.strptime('2019 04 15', '%Y %m %d')
end_date = dt.datetime.strptime('2019 04 18', '%Y %m %d')
t = np.arange(0.0, 2.0, 0.01)
dates = np.arange(start_date, end_date, np.timedelta64(1, 'h'),
dtype='datetime64')
x = np.sin(3*np.pi*t)[:72]
y = np.cos(3*np.pi*t)[:72]
z = np.cos(6*np.pi*t)[:72]
for c in [x, y, z]:
c[40:50] = np.nan
source = ColumnDataSource(data={'Date': dates, 'x': x, 'y': y, 'z': z})
p.xaxis.axis_label = 'Date'
p.yaxis.axis_label = 'Position (m)'
def add_plot(y, color):
new_plot = p.line(x='Date', y=y, line_width=1, color=color, source=source)
return new_plot
x = add_plot('x', 'red')
y = add_plot('y', 'green')
z = add_plot('z', 'blue')
checkbox = CheckboxButtonGroup(labels=['x', 'y', 'z'], active=[0, 1, 2])
checkbox.callback = CustomJS(args=dict(x=x, y=y, z=z), code="""
//console.log(cb_obj.active);
x.visible = false;
y.visible = false;
z.visible = false;
for (i in cb_obj.active) {
//console.log(cb_obj.active[i]);
if (cb_obj.active[i] == 0) {
x.visible = true;
} else if (cb_obj.active[i] == 1) {
y.visible = true;
} else if (cb_obj.active[i] == 2) {
z.visible = true;
}
}
""")
callback = CustomJS(args=dict(p=p), code="""
var a = cb_obj.value;
p.x_range.start = a[0];
p.x_range.end = a[1];
""")
range_slider = DateRangeSlider(start=start_date, end=end_date,
value=(start_date, end_date), step=1)
range_slider.js_on_change('value', callback)
def get_hovertools():
hovers = {'x': x, 'y': y, 'z': z}
for k, v in hovers.items():
hovers[k] = HoverTool(mode='vline', renderers=[v])
hovers[k].tooltips = [('Date', '#Date{%F %H:%M:%S.%u}'),
(k, '#{'+k+'}{%0.2f}m')]
hovers[k].formatters = {'Date': 'datetime', k: 'printf'}
p.add_tools(hovers[k])
get_hovertools()
# --------------------- second figure here --------------------------
p2 = figure(title="data", plot_height=400, tools=TOOLS, plot_width=1300)
start_date = dt.datetime.strptime('2019 04 15', '%Y %m %d')
end_date = dt.datetime.strptime('2019 04 18', '%Y %m %d')
t = np.arange(0.0, 2.0, 0.01)
dates = np.arange(start_date, end_date, np.timedelta64(1, 'h'),
dtype='datetime64')
x2 = [1]*72
y2 = [2]*72
z2 = [3]*72
source = ColumnDataSource(data={'Date': dates, 'x': x2, 'y': y2, 'z': z2})
def add_plot(y, color):
new_plot = p2.line(x='Date', y=y, line_width=1, color=color, source=source)
return new_plot
x2 = add_plot('x', 'red')
y2 = add_plot('y', 'green')
z2 = add_plot('z', 'blue')
layout = column(p, widgetbox(checkbox), widgetbox(range_slider),
p2)
show(layout)
This example shows how to add AND remove figure from a Bokeh document (Bokeh v1.1.0). It doesn't include widgets for clarity reason but you could add your widgets there as well using the same approach.
However, maybe you could consider tabs as an option. When using tabs you don't need to remove/add the root elements, those are continuously present on the separate tabs which user can switch. You can find tabs examples here
from bokeh.models import Select, Row, ColumnDataSource
from bokeh.plotting import figure, curdoc
import numpy as np
x = np.linspace(0, 4 * np.pi, 100)
source = ColumnDataSource(dict(x = x, y = np.cos(x)))
glyphs = ["line", "scatter"]
select = Select(title = "Select plot:", value = "", options = [""] + glyphs)
curdoc().add_root(Row(select, name = 'root'))
def callback(attr, old, new):
layouts = curdoc().get_model_by_name('root').children
for glyph in glyphs:
plot = curdoc().get_model_by_name(glyph)
if plot:
layouts.remove(plot)
if new:
p = figure(name = new)
exec(new + ' = p.' + new + '("x", "y", source = source)')
layouts.append(p)
select.on_change('value', callback)
Result:

Bokeh Multi-Select widget callback not Working

I am new to Bokeh. Trying to implement few widget callbacks recently and hard to find any resources online.
Scenario: I have a multi_select bokeh widget with the list of the companies. Based on the companies selcted, data in the Vbar has to be changed. For this, i am trying to change the datasource that is used in the Vbar based on the values from Multi select. I am unable to get desired results. Can someone please help me out with this.
I am poor at CustomJs and hence doing this on Bokeh Server for callabcks. If there is a solution on CustomJs too, that would help me a lot.
Thank you very much for your time in Advance !
Below is the code I am using
source =source1[source1['year']== dt.now().year]
sourcex = source[source['month'] ==1 &
source['CompanyNo'].isin(['01','02','03','04','05','08']) ]
Overall= ColumnDataSource(source)
Curr= ColumnDataSource(sourcex)
boolinit = source['month']==1
view = CDSView(source=Overall, filters=[BooleanFilter(boolinit)])
hover3 = HoverTool(
tooltips = [
('day', '#day'),
('ExtendedPrice','#{ExtendedPrice}{0,0}'),
],
formatters = {
'day': 'datetime',
'ExtendedPrice': 'numeral'}
)
p = figure(
title='YEARLY SALES',
plot_width=600,
plot_height=400,
min_border=3,
tools = [hover3,'box_zoom','wheel_zoom', 'pan','reset'],
toolbar_location="above")
p.vbar(x='day', top='ExtendedPrice', width=0.2, color='#e8bc76',
source=Curr)
p.xaxis.axis_label = 'Day'
p.xaxis.axis_label_text_font_style = 'normal'
p.xaxis.axis_label_text_font_size = '12pt'
p.yaxis[0].formatter = NumeralTickFormatter(format="0,0")
def Multi_Selectupdate(attrname, old, new):
curr=sourcex[sourcex['CompanyNo'].isin(new)]
source.data=curr.data
companies=['All']+sourcex['CompanyNo'].unique().tolist()
multi_select = MultiSelect(title="Select:", value=['01'], options=companies,
height=200, width=100)
multi_select.on_change('value',Multi_Selectupdate )
layout = column(multi_select, p )
show(layout)
Since I don't have the data, I generated it using following script. I have assumed your data has 3 columns - Date, ExtendedPrice, CompanyNo. I have generated first a random data of 10K rows and then summarized it at CompanyNo, day, month, year level.
import pandas as pd
import random
CopmanyList = ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15']
df = pd.DataFrame({'base' : ["2017-01-01" for t in range(10000)],
'Date' : [random.randint(0, 1035) for t in range(10000)],
'ExtendedPrice' : [random.random() for t in range(10000)],
'CompanyNo' : [CopmanyList[random.randint(0, 15)] for t in range(10000)]})
df['base'] = pd.to_datetime(df['base'])
df["Date2"] = df.apply(lambda x: x["base"] + timedelta(days=x['Date']), axis=1)
df.drop(['base', 'Date'], axis=1, inplace=True)
df.set_index('Date2', inplace=True)
df['month'] = df.index.month
df['year'] = df.index.year
df['day'] = df.index.day
source1=df.groupby(['year','month','day', 'CompanyNo'], as_index = False)['ExtendedPrice'].sum()
source =source1[source1['year']== dt.now().year]
sourcex = source[source['month'] ==1]
sourcex = sourcex[sourcex['CompanyNo'].isin(['01'])]
sourcex.sort_values(by='day', inplace=True)
Now, what you want to accomplish is that given a day and set of company names, you need to publish a bar chart of some type of aggregation of ExtendedPrice, i.e. say there are 2 rows for company '01' and '02' (which are selected), and for month 2 (that is also selected by a slider) for current year, the ExtendedPrice value for these two is say 100, and 200. In case we want to show a sum, you need to show 300 in barchart. This summary, you need to calculate dynamically.
There are two ways to accomplish it.
1. Bokeh Callbacks
This will use native python functions, but you need to run it with bokeh serve , e.g bokeh serve --show <filename.py>. See the code below, a native python function compsel is filtering pandas dataframe and summarizing it and replacing the chart backend data with newly created data -
from datetime import timedelta
from datetime import datetime as dt
from bokeh.models.widgets import MultiSelect, Slider
from bokeh.layouts import widgetbox, column
from bokeh.models.ranges import FactorRange
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, HoverTool, CustomJS
Overall= ColumnDataSource(source)
Curr= ColumnDataSource(sourcex[['day', 'ExtendedPrice']])
Curr.remove('index')
hover3 = HoverTool(tooltips = [('day', '#day'),('Sales','#{ExtendedPrice}{0,0.00}')],
formatters = {'day': 'datetime','Sales': 'numeral'})
p = figure(title='YEARLY SALES', plot_width=600, plot_height=400, min_border=3,
tools = [hover3,'box_zoom','wheel_zoom', 'pan','reset'],
toolbar_location="above")
r = p.vbar(x='day', top='ExtendedPrice', width=0.2, color='#e8bc76', source=Curr)
p.xaxis.axis_label = 'Day'
p.xaxis.axis_label_text_font_style = 'normal'
p.xaxis.axis_label_text_font_size = '12pt'
def compsel(attr, old, new):
vcomp = multi_select.value
vmonth = slider.value
sourcex = source[source['month'] ==vmonth]
sourcex = sourcex[sourcex['CompanyNo'].isin(vcomp)]
sourcex = sourcex.groupby(['day'], as_index = False)['ExtendedPrice'].sum()
Currnew= ColumnDataSource(sourcex[['day', 'ExtendedPrice']])
Currnew.remove('index')
r.data_source.data=Currnew.data
companies=source['CompanyNo'].unique().tolist()
companies.sort()
multi_select = MultiSelect(title="Select:", value=['01'], options=companies, height=200, width=100)
slider = Slider(start=1, end=12, value=1, step=1, title="Month")
slider.on_change("value", compsel)
multi_select.on_change("value", compsel)
layout = column(multi_select, slider, p)
curdoc().add_root(layout)
There are other options to host this application. See other option by typing bokeh serve --help in terminal
2. JavaScript Callbacks
This will generate a javascript function to interact with the chart, which will not require bokeh server and work directly on the browser. This will require very simple code in javascript. In the example solved here, I am summarizing ExtendedPrice as sum, but code will require small tweaks to calculate other stats e.g. mean. The js callback function here does the similar thing as bokeh callback function, it reads the larger datasets and filters it based on the select and slider values -
source =source1[source1['year']== dt.now().year]
sourcex = source[source['month'] ==1]
sourcex = sourcex[sourcex['CompanyNo'].isin(['01'])]
sourcex.sort_values(by='day', inplace=True)
Overall= ColumnDataSource(source)
Curr= ColumnDataSource(sourcex[['day', 'ExtendedPrice']])
Curr.remove('index')
hover3 = HoverTool(tooltips = [('day', '#day'),('Sales','#{ExtendedPrice}{0,0.00}')],
formatters = {'day': 'datetime','Sales': 'numeral'})
p = figure(title='YEARLY SALES', plot_width=600, plot_height=400, min_border=3,
tools = [hover3,'box_zoom','wheel_zoom', 'pan','reset'],
toolbar_location="above")
r = p.vbar(x='day', top='ExtendedPrice', width=0.2, color='#e8bc76', source=Curr)
p.xaxis.axis_label = 'Day'
p.xaxis.axis_label_text_font_style = 'normal'
p.xaxis.axis_label_text_font_size = '12pt'
callms = CustomJS(args=dict(source=Overall, sc=Curr), code="""
var comp=msel.attributes.value;
var f = slider.value;
sc.data['ExtendedPrice'] = [];
sc.data['day'] = [];
for (var i = 0; i <= source.get_length(); i++){
if (source.data['month'][i] == f){
if (comp.indexOf(source.data['CompanyNo'][i]) >=0){
var d1 = source.data['day'][i]
if(typeof sc.data['day'][d1-1]=="undefined"){
sc.data['day'][d1-1] = d1
sc.data['ExtendedPrice'][d1-1] = source.data['ExtendedPrice'][i]
}
else{
sc.data['ExtendedPrice'][d1-1] = sc.data['ExtendedPrice'][d1-1] + source.data['ExtendedPrice'][i]
}
}
}
}
sc.change.emit();
""")
companies=source['CompanyNo'].unique().tolist()
companies.sort()
multi_select = MultiSelect(title="Select:", value=['01'], options=companies, height=200, width=100, callback=callms)
slider = Slider(start=1, end=12, value=1, step=1, title="Month", callback=callms)
callms.args["msel"] = multi_select
callms.args["slider"] = slider
layout = column(multi_select, slider, p)
output_file("Filterdata.html")
show(layout)

Bokeh Hovering only Red candles are shown

I am learning Bokeh, I am trying to plot a data from a panda data frame in a candlestick chart. Then hover over the candle to see the data. In the following code i see only red candles( open > close) working for hovering and not the one where (open < close) >not sure what is the problem.
Please let me know what is the issue with this.
from math import pi
import pandas as pd
from bokeh.models.formatters import TickFormatter, String, List
from bokeh.plotting import figure, show, output_file
from bokeh.sampledata.stocks import MSFT
from bokeh.models import ColumnDataSource, HoverTool
# create a custom model for a new tick formatter
class DateGapTickFormatter(TickFormatter):
''' A custom TickFormatter useful for skipping dates
Axis labels are taken from an array of date strings
(e.g. ['Sep 01', 'Sep 02', ...]) passed to the ``date_labels``
property.
'''
date_labels = List(String, help="""
An array of date strings to map integer date indices to.
""")
__implementation__ = """
import {Model} from "model"
import * as p from "core/properties"
export class DateGapTickFormatter extends Model
type: 'DateGapTickFormatter'
doFormat: (ticks) ->
date_labels = #date_labels
return (date_labels[tick] ? "" for tick in ticks)
#define {
date_labels: [ p.Any ]
}
"""
df = pd.DataFrame(MSFT)[:100]
# xaxis date labels used in the custom TickFormatter
date_labels = [date.strftime('%b %d') for date in pd.to_datetime(df["date"])]
inc = df.close > df.open
dec = df.open > df.close
w = 0.5
TOOLS = "pan,hover,wheel_zoom,box_zoom,reset,save"
p = figure(tools=TOOLS, plot_width=1000, title="MSFT Candlestick with Custom X-Axis")
hover = p.select(dict(type=HoverTool))
hover.mode ="mouse"
#hover.names = ['vbar1','vbar2']
hover.tooltips=[
( 'Date', '#date' ),
( 'Close','#close{0.2f}' ),
# ( 'Open', '#open{0.2f}'),
# ( 'High','#High{%0.2f}'),
# ( 'Low', '#Low{%0.2f}'),
# ( 'Delta', '#Delta{0.2f}' ),
# ( 'volume', '#Volume' )
]
mysource1 = ColumnDataSource(df[inc])
mysource2 = ColumnDataSource(df[dec])
p.xaxis.major_label_orientation = pi/4
p.grid[0].ticker.desired_num_ticks = 6
# use the custom TickFormatter. You must always define date_labels
p.xaxis.formatter = DateGapTickFormatter(date_labels=date_labels)
# x coordinates must be integers. If, for example, df.index are
# datetimes, you should replace them with a integer sequence
p.segment(df.index, df.high, df.index, df.low, color="black")
p.vbar(df.index[inc], w, df.open[inc], df.close[inc], fill_color="#D5E1DD", line_color="black",source=mysource1)
p.vbar(df.index[dec], w, df.open[dec], df.close[dec], fill_color="#F2583E", line_color="black",source=mysource2)
output_file("custom_datetime_axis.html", title="custom_datetime_axis.py example")
show(p) # open a browser

Bokeh combine line and bar chart with hover

I want to create a combined bar and line chart with a hover tool. As I wanted to add a hover tool I initially created a figure and then tried to add the bars with vbar and the line with line_glyph.
This doesnt work, as it only creates a blank white canvas.
from bokeh.charts import Bar, output_file, show
from bokeh.plotting import figure
from bokeh.models.ranges import Range1d
from bokeh.models import ColumnDataSource, HoverTool
from bokeh.models.glyphs import Line as Line_glyph
import pandas as pd
import numpy as np
data_2015_2016=pd.DataFrame({
'year':[2015,2015,2015,2015,2015],
'volume_neutral':[420,430,440,400,np.nan],
'volume_promo':[np.nan,np.nan,np.nan,np.nan,2000],
'volume_neutral_promo': [420,430,440,400,2000],
'Promo':['No','No','No','No','Yes'],
'Product':['Lemonade','Lemonade','Lemonade','Lemonade','Lemonade'],
'yw':['2015-W01','2015-W02','2015-W03','2015-W04','2015-W05']
})
hover=HoverTool(
tooltips=[
( 'Date', '#yw' ),
( 'Volume (in kg)', '#volume_neutral_promo' ), # use #{ } for field names with spaces
( 'Promo', '#Promo' ),
( 'Product', '#Product' )
])
p = figure(plot_width=1000, plot_height=800, tools=[hover],
title="Weekly Sales 2015-2016",toolbar_location="below")
source = ColumnDataSource(data=data_2015_2016)
#Bar Chart
#This worked however I donno how to combine it with a hoover
#p.Bar = Bar(data_2015_2016, label='yw', values='volume_promo', title="Sales",legend=False,plot_width=1000, plot_height=800)
p.vbar(x='yw', width=0.5, bottom=0,top='volume_promo', color="firebrick",source=source)
# create a line glyph object which references columns from source data
glyph = Line_glyph(x='yw', y='volume_neutral', line_color='green', line_width=2)
# add the glyph to the chart
p.add_glyph(source, glyph)
p.xaxis.axis_label = "Week and Year"
# change just some things about the y-axes
p.yaxis.axis_label = "Volume"
p.yaxis.major_label_orientation = "vertical"
p.y_range = Range1d(0, max(data_2015_2016.volume_neutral_promo))
output_file("bar_line.html")
show(p)
There are a few things wrong with your code:
You've mixed up some of your column names, e.g. volume_neutral_promo is what is actually in your data source, but instead you have the glyphs mistakenly reference volume_promo_neutral
If you want to use a categorical range with bokeh.plotting plots, you have to explicitly tell that:
p = figure(plot_width=1000, plot_height=800, tools=[hover],
title="Weekly Sales 2015-2016",toolbar_location="below",
# this part is new
x_range=['2015-W01','2015-W02','2015-W03','2015-W04','2015-W05'])
Here is your complete code updated. I have removed the parts about bokeh.charts as I would not recommend using that API anymore (it's basically unmaintained at this point). Also I used the simpler p.line instead of the low level Line glyph:
from numpy import nan
import pandas as pd
from bokeh.io import output_file, show
from bokeh.models import ColumnDataSource, HoverTool
from bokeh.plotting import figure
data_2015_2016 = pd.DataFrame({
'year': [2015, 2015, 2015, 2015, 2015],
'volume_neutral': [420, 430, 440, 400, nan],
'volume_promo': [nan, nan, nan, nan, 2000],
'volume_neutral_promo': [420, 430, 440, 400, 2000],
'Promo': ['No', 'No', 'No', 'No', 'Yes'],
'Product': ['Lemonade', 'Lemonade', 'Lemonade', 'Lemonade', 'Lemonade'],
'yw': ['2015-W01', '2015-W02', '2015-W03', '2015-W04', '2015-W05']
})
source = ColumnDataSource(data=data_2015_2016)
hover=HoverTool(tooltips=[
( 'Date', '#yw' ),
( 'Volume (in kg)', '#volume_neutral_promo' ),
( 'Promo', '#Promo' ),
( 'Product', '#Product' ),
])
p = figure(plot_width=1000, plot_height=800, tools=[hover],
title="Weekly Sales 2015-2016",toolbar_location="below",
x_range=['2015-W01', '2015-W02', '2015-W03', '2015-W04', '2015-W05'])
p.vbar(x='yw', width=0.5, bottom=0, top='volume_promo', color="firebrick", source=source)
p.line(x='yw', y='volume_neutral', line_color='green', line_width=2, source=source)
p.xaxis.axis_label = "Week and Year"
p.yaxis.axis_label = "Volume"
p.yaxis.major_label_orientation = "vertical"
p.y_range.start = 0
p.y_range.range_padding = 0
output_file("bar_line.html")
show(p)
This results in the following plot with hover tool:

Categories

Resources