categorized barchart in bokeh python - python

I am trying to plot 3 level category in bokeh python, but I am facing issue and my plot is not working, Could you give me a hand on this ?
below is my code :
from bokeh.models import ColumnDataSource, FactorRange
from bokeh.io import show
p = figure(plot_height=250, title="Fruit Counts by Year")
#tp is my data as pandas DataFrame
x1=list(tp['SRN'])
x2=list(tp['SN'])
x3=list(tp['PN'])
counts = list(tp['VS.FEGE.RXMAXSPEED'])
x=[("SRN"+str(a1),"SN"+str(a2),"pN"+str(a3)) for a1,a2,a3 in zip(x1,x2,x3)]
source = ColumnDataSource(data=dict(x=x, counts=counts))
p =figure(x_range=FactorRange(*x),plot_height=250,title="title",toolbar_location=None, tools="")
p.vbar(x='x', top='counts', width=0.9,source=source)
show(p)
tp data is as below :
SRN SN PN VS.FEGE.RXMAXSPEED VS.FEGE.TXMAXSPEED
0 0 18 0 1.794 0.307
1 0 18 1 1.896 0.307
2 0 19 0 131238.122 574793.502
3 0 19 1 31806.984 126149.078
4 0 20 0 4.968 0.307
I am not receiving any specific error, just plot is not shown.
Also I want to know how "x_range=FactorRange(*x)" is working and is there any alernative way instead ?

From the looks of it, you have not imported Bokeh.Plotting which defines the figure.
from bokeh.models import ColumnDataSource, FactorRange
from bokeh.io import show
from bokeh import plotting
p = plotting.figure(plot_height=250, title="Fruit Counts by Year")
Call "figure" function like this whereever you are using it. It will resolve the "Figure not defined" error which is occurring in your case.

I found the issue,
it is due to that x_range key argument is defined again in figure, if x_range is referenced later based on data, it will solve the issue :
p =figure(x_range=FactorRange(*x),plot_height=250,title="title",toolbar_location=e, tools="")
p.x_range.factors = x

Related

Display hours,minutes,seconds,msecs on xaxis in bokeh plot

I want to display xaxis in below format. It should include hr,min,secs,msecs format
14:38:21:701, 14:38:21:702..
Below is the code I have written
#!/usr/bin/python3
from bokeh.plotting import figure, output_file, show
import pandas as pd
from bokeh.models import DatetimeTickFormatter, ColumnDataSource, PrintfTickFormatter
from pandas import ExcelWriter, ExcelFile
weight = pd.read_excel('file_name.xlsx')
source = ColumnDataSource(weight)
#Take data and present in a graph
output_file("test.html")
p = figure(plot_width=1500, plot_height=400)
p.left[0].formatter.use_scientific = False
p.xaxis[0].formatter.use_scientific = False
#Need to modify this format to display hours,minutes,seconds,msecs
p.xaxis[0].formatter = PrintfTickFormatter(format="%sms")
p.scatter(x='Time',y='Bearer ID 5 WM Low', color = "navy",source=source,legend_label='Bearer ID 5 WM Low')
p.scatter(x='Time',y='Bearer ID 5 VM Count', color = "red",source=source,legend_label='Bearer ID 5 VM Count')
show(p)
Below is the output for it.
You could use DatetimeFormatter instead of PrintfTickFormatter.
Visit DatetimeTickFormatter for the documentation

Bokeh + Python - hover over vbar, while data coming from pandas

I have a pandas dataframe I am pulling data from and showing as a bar plot using Bokeh. What I want is show the max value of each bar upon hover. This is the first day I'm using Bokeh and I already changed the code a couple times and I'm really confused how to set it up. I added the:
p.add_tools(HoverTool(tooltips=[("x_ax", "#x_ax"), ("y_ax", "#y_ax")]))
line, but just don't understand it.
Here's the code:
from bokeh.plotting import figure, output_file, show
from bokeh.models import ColumnDataSource, ranges, LabelSet
from bokeh.plotting import figure, save, gridplot, output_file
# prepare some data
# x = pd.Series(range(1,36))
x_ax = FAdf['SampleID']
y_ax = FAdf['First Run Au (ppm)']
# output to static HTML file
output_file("bars.html")
# create a new plot with a title and axis labels
p = figure(x_range=x_ax, title="Batch results", x_axis_label='sample', y_axis_label='Au (ppm)',
toolbar_location="above", plot_width=1200, plot_height=800)
p.add_tools(HoverTool(tooltips=[("x_ax", "#x_ax"), ("y_ax", "#y_ax")]))
# setup for the bars
p.vbar(x=x_ax, top=y_ax, width=0.9)
p.xgrid.grid_line_color = None
p.y_range.start = 0
# turn bar tick labels 45 deg
p.xaxis.major_label_orientation = np.pi/3.5
# show the results
show(p)
Sample from the FAdf database:
SampleID:
0 KR-19 349
1 KR-19 351
2 Blank_2
3 KR-19 353
First Run Au (ppm):
0 0.019
1 0.002
2 0.000
3 0.117
If you pass actual literal data sequences to a glyph method like you have above, then Bokeh uses generic field names like "x" and "y" since it has no way of knowing any other names use. These are the columns you would need to configure the hover tool with:
tooltips=[("x_ax", "#x"), ("y_ax", "#y")])
Alternatively, you can pass a source argument to the vbar method so that the columns have the column names that you prefer. This is described in the Users Guide:
https://docs.bokeh.org/en/latest/docs/user_guide/data.html

show the labels of Bokeh datetime axis in exact position of points

I'm using the datetime axis of Bokeh. In the Bokeh data source, I have my x in numpy datetime format and others are y numbers. I'm looking for a way to show the label of the x datetimx axis right below the point. I want Bokeh to show the exact datetime that I provided via my data source, not some approximation! For instance, I provide 5:15:00 and it shows 5:00:00 somewhere before the related point.I plan to stream data to the chart every 1 hour, and I want to show 5 points each time. Therefore, I need 5 date-time labels. How can I do that? I tried p.yaxis[0].ticker.desired_num_ticks = 5 but it didn't help. Bokeh still shows as many number of ticks as it wants! Here is my code and result:
import numpy as np
from bokeh.models.sources import ColumnDataSource
from bokeh.plotting import figure
from bokeh.io import show
from bokeh.palettes import Category10
p = figure(x_axis_type="datetime", plot_width=800, plot_height=500)
data = {'x':
[np.datetime64('2019-01-26T03:15:10'),
np.datetime64('2019-01-26T04:15:10'),
np.datetime64('2019-01-26T05:15:10'),
np.datetime64('2019-01-26T06:15:10'),
np.datetime64('2019-01-26T07:15:10')],
'A': [10,25,15,55,40],
'B': [60,50,80,65,120],}
source = ColumnDataSource(data=data)
cl = Category10[3][1:]
r11 = p.line(source=source, x='x', y='A', color=cl[0], line_width=3)
r12 = p.line(source=source, x='x', y='B', color=cl[1], line_width=3)
p.xaxis.formatter=DatetimeTickFormatter(
seconds=["%H:%M:%S"],
minsec=["%H:%M:%S"],
minutes=["%H:%M:%S"],
hourmin=["%H:%M:%S"],
hours=["%H:%M:%S"],
days=["%H:%M:%S"],
months=["%H:%M:%S"],
years=["%H:%M:%S"],
)
p.y_range.start = -100
p.x_range.range_padding = 0.1
p.yaxis[0].ticker.desired_num_ticks = 5
p.xaxis.major_label_orientation = math.pi/2
show(p)
and here is the result:
As stated in the docs, num_desired_ticks is only a suggestion. If you want a ticks at specific locations that do not change, then you can use a FixedTicker, which can be set by plain list as convenience:
p.xaxis.ticker = [2, 3.5, 4]
For datetimes, you would pass the values as milliseconds since epoch.
If you want a fixed number of ticks, but the locations may change (i.e. because the range may change), then there is nothing built in to do that. You could make a custom ticker extension.

Verbatim labels in legend in bokeh plots

I'm trying to use bokeh in python for interactive analysis of my plots.
My data are stored in pandas.Dataframe. I'd like to have a legend with column names as labels. However, bokeh extracts values from respective column instead.
import pandas as pd
from bokeh.plotting import figure
from bokeh.io import output_notebook, show
from bokeh.models import ColumnDataSource
output_notebook()
BokehJS 0.12.13 successfully loaded.
df = pd.DataFrame({'accuracy': np.random.random(10)}, index=pd.Index(np.arange(10), name='iteration'))
df
output:
accuracy
iteration
0 0.977427
1 0.057319
2 0.307741
3 0.127390
4 0.662976
5 0.313618
6 0.214040
7 0.214274
8 0.864432
9 0.800101
Now plot:
p = figure(width=900, y_axis_type="log")
source = ColumnDataSource(df)
p.line(x='iteration', y='accuracy', source=source, legend='accuracy')
show(p)
Result:
Desired output, obtained with adding space: legend='accuracy'+' ':
Although I've reached my goal, the method does not satisfy me. I think, there should be more elegant and official way to tell between column name and legend label.
There is. Bokeh tries to "do the right thing" in most situations, but doing that makes for a few corner cases where the behavior is less desirable, and this is one of them. However, specifically in this instance, you can always be explicit about whether the string is to be interpreted as a value or as field:
from bokeh.core.properties import value
p.line(x='iteration', y='accuracy', source=source, legend=value('accuracy'))

How do I make my Bokeh Boxplot show the values of all the tick marks on the y-axis?

I am running Python in Jupyter Notebook and I have the following codes running fine in the Notebook:
from bokeh.charts import BoxPlot, show
from bokeh.io import output_notebook
output_notebook ()
df = myfile2
p = BoxPlot(df, values='Total Spending', label=['Market'],color='Market', marker='square',
whisker_color='black',legend=False, plot_width=800, plot_height=600,
title="Total Spending, February 2017)")
p.xaxis.major_label_orientation = "horizontal"
show(p)
My issue is that the y-axis is displaying the following values and tick marks:
1000-
-
-
-
-
500-
-
-
-
-
0-
I would like to format that y-axis so that the values show up as follows:
1000
900
800
700
...
0
Can it be done in Bokeh?
So, I had the same issue and found a solution in this threat: https://stackoverflow.com/a/27878536/2806632
Basically, what you want is to create your figure without an axis and then add an Axis with your format. Something on the lines of:
from bokeh.models import SingleIntervalTicker, LinearAxis
from bokeh.charts import BoxPlot, show
from bokeh.io import output_notebook
output_notebook ()
df = myfile2
# See that x_axis_type is now None
p = BoxPlot(df, values='Total Spending', label=['Market'],color='Market', marker='square',
whisker_color='black',legend=False, plot_width=800, plot_height=600,
title="Total Spending, February 2017)", x_axis_type=None)
# Interval one, assuming your values where already (0,100,200...)
ticker = SingleIntervalTicker(interval=1, num_minor_ticks=0)
yaxis = LinearAxis(ticker=ticker)
p.add_layout(yaxis, 'left')
# I'm pretty sure you won't need this: p.xaxis.major_label_orientation = "horizontal"
show(p)

Categories

Resources