I'm trying to plot some data with Bokeh via pandas. The x-axis is date, and I can get Bokeh to plot the axis "mostly" correct (the range may be off). However, the line it outputs is all over the place.
For example:
It looks like maybe it's one big, continuous line?
Here's my code:
# library imports
import pandas as pd
from bokeh.io import output_file, show, vform
from bokeh.plotting import figure, output_file, ColumnDataSource, show
from bokeh.models import HoverTool, BoxAnnotation, BoxSelectTool, BoxZoomTool, WheelZoomTool, ResetTool
# Import csv into pandas dataframe
df = pd.read_csv(r"C:\Users\paul.shapiro\Documents\kwdata.csv", parse_dates=['Interest over time_time'])
df.rename(columns={'Search Term': 'keyword', 'Interest over time_time': 'date', 'Weekly Volume': 'volume'}, inplace=True)
source = ColumnDataSource(data=dict(x=df['date'], y=df['volume'], desc=df['keyword']))
TOOLS = [HoverTool(tooltips=[("Keyword", "#desc"),("Date", "#x"),("Search Volume", "#y")]), BoxZoomTool(), WheelZoomTool(), ResetTool()]
# Output html for embedding
output_file("line.html")
p = figure(plot_width=800, plot_height=800, tools=TOOLS, x_axis_type="datetime")
# add both a line and circles on the same plot
p.line(df['date'], df['volume'], line_width=2, color=df['keyword'], source=source)
p.circle(df['date'], df['volume'], fill_color="white", size=8, source=source)
show(p)
It's also interesting to note, that if you plot it using bokeh.charts (if I did this the tooltips wouldn't work, so it's not an option), it plots fine:
defaults.width = 800
defaults.height = 800
TOOLS = [BoxZoomTool(), WheelZoomTool(), ResetTool()]
line = Line(df, x='date', y='volume', color='keyword', source=source, tools=TOOLS)
show(line)
output_file("line.html", title="Search Volume")
Any help would be much appreciated. This has been driving me crazy!
SOLVED using multi_line() and a for loop:
import pandas as pd
from bokeh.io import output_file, show, vform
from bokeh.plotting import figure, output_file, ColumnDataSource, show
from bokeh.models import HoverTool, BoxAnnotation, BoxSelectTool, BoxZoomTool, WheelZoomTool, ResetTool
df = pd.read_csv(r"C:\Users\paul.shapiro\Documents\kwdata.csv", parse_dates=['Interest over time_time'])
df.rename(columns={'Search Term': 'keyword', 'Interest over time_time': 'date', 'Weekly Volume': 'volume'}, inplace=True)
gp = df.groupby('volume')
source = ColumnDataSource(data=dict(x=df['date'], y=df['volume'], desc=df['keyword']))
TOOLS = [HoverTool(tooltips=[("Keyword", "#desc"),("Date", "#x"),("Search Volume", "#y")]), BoxZoomTool(), WheelZoomTool(), ResetTool()]
p = figure(plot_width=800, plot_height=800, tools=TOOLS, x_axis_type="datetime")
gp = df.groupby('keyword')
# groups() returns a dict with 'Gene':indices as k:v pair
for g in gp.groups.items():
p.multi_line(xs=[df.loc[g[1], 'date']], ys=[df.loc[g[1], 'volume']])
p.circle(df['date'], df['volume'], fill_color="white", size=8, source=source)
output_file("newline.html")
show(p)
I cannot see anything wrong with your code. Try to see how different the dataframe df is from a simple nested list of values as per the bokeh example. Maybe by doing some manipulation to the dataframe you can get this working.
http://docs.bokeh.org/en/latest/docs/reference/plotting.html
from bokeh.plotting import figure, output_file, show
p = figure(plot_width=300, plot_height=300)
p.multi_line(xs=[[1, 2, 3], [2, 3, 4]], ys=[[6, 7, 2], [4, 5, 7]],
color=['red','green'])
show(p)
Related
In reference to
https://docs.bokeh.org/en/latest/docs/gallery/range_tool.html
where you have the range tool control the main top chart.
Can you modify this so that you can select over several charts? So far what I tried displays the charts but only the chart I synch with x_range is the chart that moves. I tried passing a list, a series, nothing works. Can someone assist?
Sample code:
import numpy as np
from bokeh.io import show
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, RangeTool
from bokeh.plotting import figure
from bokeh.sampledata.stocks import AAPL, GOOG
from bokeh.layouts import gridplot
dates = np.array(AAPL['date'], dtype=np.datetime64)
source = ColumnDataSource(data=dict(date=dates, aapl=AAPL['adj_close'], goog=GOOG['adj_close']))
p1 = figure(plot_height=300, plot_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]))
p1.line('date', 'aapl', source=source)
p1.yaxis.axis_label = 'Price'
p2 = figure(plot_height=300, plot_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]))
p2.line('date', 'goog', source=source)
p2.yaxis.axis_label = 'Price'
p = gridplot([[p1,p2]])
select = figure(title="Drag the middle and edges of the selection box to change the range above",
plot_height=130, plot_width=1600, y_range=p1.y_range,
x_axis_type="datetime", y_axis_type=None,
tools="", toolbar_location=None, background_fill_color="#efefef")
range_tool = RangeTool(x_range=p1.x_range)
range_tool.overlay.fill_color = "navy"
range_tool.overlay.fill_alpha = 0.2
select.line('date', 'aapl', source=source)
select.line('date', 'goog', source=source)
select.ygrid.grid_line_color = None
select.add_tools(range_tool)
select.toolbar.active_multi = range_tool
show(column(p, select))
Output:
You will also have to configure all the plots that you want to be synchronized, with the same range e.g.
p2 = figure(..., x_range=p1.x_range)
This is a follow-up question of this one.
I would like to add text to the cells in the heatmap. I thought I could use LabelSet as described here. However, unfortunately, I don't see any labels when I run the following code:
import pandas as pd
from bokeh.io import show
from bokeh.models import (CategoricalColorMapper, LinearColorMapper,
BasicTicker, PrintfTickFormatter, ColorBar,
ColumnDataSource, LabelSet)
from bokeh.plotting import figure
from bokeh.palettes import all_palettes
from bokeh.transform import transform
df = pd.DataFrame({
'row': list('xxxxxxyyyyyyzzzzzz'),
'column': list('aabbccaabbccaabbcc'),
'content': ['c1', 'c2', 'c3', 'c1', 'c2', 'c3'] * 3,
'amount': list('123212123212123212')})
df = df.drop_duplicates(subset=['row', 'column'])
source = ColumnDataSource(df)
rows = df['row'].unique()
columns = df['column'].unique()
content = df['content'].unique()
colors = all_palettes['Viridis'][max(len(content), 3)]
mapper = CategoricalColorMapper(palette=colors, factors=content)
TOOLS = "hover,save,pan,box_zoom,reset,wheel_zoom"
p = figure(title="My great heatmap",
x_range=rows, y_range=columns,
x_axis_location="above", plot_width=600, plot_height=400,
tools=TOOLS, toolbar_location='below',
tooltips=[('cell content', '#content'), ('amount', '#amount')])
p.grid.grid_line_color = None
p.axis.axis_line_color = None
p.axis.major_tick_line_color = None
p.axis.major_label_text_font_size = "5pt"
p.axis.major_label_standoff = 0
p.rect(x="row", y="column", width=1, height=1,
source=source,
fill_color=transform('content', mapper))
labels = LabelSet(x='row', y='column', text='content', level='glyph',
x_offset=1, y_offset=1, source=source,
render_mode='canvas')
p.add_layout(labels)
show(p)
I see the heatmap, but no labels. How can I display the text?
There are five levels: "image, underlay, glyph, annotation, overlay". The level of p.rect is glyph,
if you don't set the level argument of LabelSet, the level of it is annotation, which is on top of
the level glyph.
Interestingly, OP's code worked for me. I came here because I had the same problem. Turns out that the annotation data should be a string. After converting the respective column in ColumnDataSource() my annotations (numbers) showed up in the heatmap.
I have the following problem when using bokeh's TapTool and DataTable together:
As soon as one dot is clicked (TapTool used), selections on the DataTable do no longer get represented in the plot anymore.
Here is the minimal example for a Jupyter Notebook:
from bokeh.plotting import show, output_notebook, figure
from bokeh.models import ColumnDataSource, OpenURL, TapTool
from bokeh.layouts import widgetbox, column
from bokeh.models.widgets import DataTable, TableColumn
source = ColumnDataSource(dict(
x=[1, 2, 3, 4, 5],
y=[6, 7, 2, 4, 5],
url=["http://www.stackoverflow.com"]*5))
p = figure(plot_width=400,
plot_height=400,
tools="tap,reset",
)
p.circle(source=source,
x="x",
y="y",
size=20,
color="navy",
alpha=0.5)
columns = [
TableColumn(field="x", title="X-Value"),
TableColumn(field="y", title="Y-Value"),
TableColumn(field="url", title="URL")
]
data_table = DataTable(source=source, columns=columns,
width=400, height=400)
url = "#url"
taptool = p.select(type=TapTool)
taptool.callback = OpenURL(url=url)
output_notebook()
show(column(p, widgetbox(data_table)))
Expected behaviour: Selections from the DataTable get represented in the plot like before clicking one dot.
Thank you all in advance.
Problem solved. This was an issue in bokeh 0.13
With anaconda I couldn't update to 1.0.1, so I created a new environment, where bokeh can be 1.0.1 an the problem no longer exists.
I using the bokeh vbar chart tool for my energy datas. If I use the tuple(string,string,..) for xaxis then it successfully worked. But I use the datetimetickformatter for xaxis then hover tool never display.
My sample code is here:
from bokeh.io import show, output_file
from bokeh.models import ColumnDataSource, DatetimeTickFormatter,HoverTool
from bokeh.plotting import figure
from datetime import datetime
output_file("bar_colormapped.html")
dt1=datetime(2018,8,1)
dt2=datetime(2018,8,2)
dt3=datetime(2018,8,3)
dt4=datetime(2018,8,4)
dt5=datetime(2018,8,5)
dt6=datetime(2018,8,6)
fruits = [dt1,dt2,dt4,dt5,dt6]
counts = [5, 3, 4, 4, 6]
source = ColumnDataSource(data=dict(fruits=fruits, counts=counts))
tooltips=[
("val", "#counts")
]
p = figure(plot_height=350, toolbar_location=None, title="Fruit Counts",x_axis_type='datetime',tooltips=tooltips)
p.vbar(x='fruits', top='counts', width=0.9, source=source)
p.xaxis.formatter=DatetimeTickFormatter(
minutes=["%M"],
hours=["%H:%M"],
days=["%d/%m/%Y"],
months=["%m/%Y"],
years=["%Y"],
)
p.xgrid.grid_line_color = None
p.y_range.start = 0
p.y_range.end = 9
p.legend.orientation = "horizontal"
p.legend.location = "top_center"
show(p)
This is in the documentation:
https://docs.bokeh.org/en/latest/docs/user_guide/tools.html#formatting-tooltip-fields
Presumably in your specific case, something like:
hover = HoverTool(tooltips=[('date', '#fruits{%F}'), ('val', '#counts')],
formatters=dict(fruits='datetime'))
p.add_tools(hover)
Also your bars are far too thin for hit testing. The units on a datetime scale is milliseconds since epoch, but your range covers many months. To the bars need to be much wider to show up on that scale. E.g. width=10000000 yields:
I'm trying to make a an interactive plots with bokeh and the hover tool.
More precisely, I'm trying to make a plot like the one I made in seaborn but I'd like it to be more interactive, meaning :
I'd like people to see the income level when they hover over one point.
I'd like the plot to stay scattered like that such thas each point is an individual point, letting people hover over them in the process.
I'd like to pick the colors,to divide between different levels of income.
How would I do that ? I tried this :
x = Belgian_income["Municipalities"]
y = Belgian_income["Average income per inhabitant"]
list_x = list(x)
list_y = list(y)
dict_xy = dict(zip(list_x,list_y))
output_file('test.html')
source = ColumnDataSource(data=dict(x=list_x,y=list_y,desc=str(list_y)))
hover = HoverTool(tooltips=[
("index", "$index"),
("(x,y)", "($x, $y)"),
('desc','#desc'),
])
p = figure(plot_width=400, plot_height=400, tools=[hover],
title="Belgian test")
p.circle('x', 'y', size=20, source=source)
show(p)
But it doesn't work at all, can someone help me ? Thanks a lot.
The main issue in your code is that you provide lists to all columns of the data source, except for the desc - you provide a single string there.
With that fixed, your code works. But the tooltips show X and Y coordinates of the mouse pointer - not the actual data. For the actual data, you have to replace $ in the hover tooltips definitions with #.
Consider this working example:
from math import sin
from random import random
from bokeh.io import output_file, show
from bokeh.models import ColumnDataSource, HoverTool, LinearColorMapper
from bokeh.palettes import plasma
from bokeh.plotting import figure
from bokeh.transform import transform
list_x = list(range(100))
list_y = [random() + sin(i / 20) for i in range(100)]
desc = [str(i) for i in list_y]
source = ColumnDataSource(data=dict(x=list_x, y=list_y, desc=desc))
hover = HoverTool(tooltips=[
("index", "$index"),
("(x,y)", "(#x, #y)"),
('desc', '#desc'),
])
mapper = LinearColorMapper(palette=plasma(256), low=min(list_y), high=max(list_y))
p = figure(plot_width=400, plot_height=400, tools=[hover], title="Belgian test")
p.circle('x', 'y', size=10, source=source,
fill_color=transform('y', mapper))
output_file('test.html')
show(p)