Cannot update Bokeh chart with CheckboxGroup - python

I want to make interactive bokeh line charts. I use CheckboxGroup widget to update the charts. However, the charts won't update.
d={'created':['02/01/2019 00:00:00','03/01/2019 00:00:00','04/01/2019 00:00:00','05/01/2019 00:00:00','06/01/2019 00:00:00','07/01/2019 00:00:00'],
'aaa': [5, 4, 10, 7, 5, 5],
'bbb':[0,10,2,9,8,4],
'ccc':[10,12,14,14,5,7]}
df=pd.DataFrame.from_dict(d)
df['created']=pd.to_datetime(df['created'])
df.set_index('created', inplace=True)
plot=figure(plot_width=700,
plot_height=500,
x_axis_type='datetime',
title='lines')
src=ColumnDataSource(df)
products=sorted(list(src.data.keys())[1:])
product_selection=CheckboxGroup(labels=products, active =[0,1])
def make_dataset(initial_dataframe, columns_to_keep):
df=initial_dataframe[columns_to_keep].copy()
src=ColumnDataSource(df)
return src
for i in product_selection.active:
plot.line(x='created', y=product_selection.labels[i], source=src)
def update(attr, old, new):
prods=[product_selection.labels[i] for i in product_selection.active]
src=make_dataset(df,prods)
product_selection.on_change('active', update)
layout=row(plot,product_selection)
curdoc().add_root(layout)
Please help me to correct my code.

If you want to show / hide the lines at checkbox selection then you could do it this way by using visibility attribute of a glyph (run the code with bokeh serve --show app.py):
from bokeh.models import CheckboxGroup, ColumnDataSource, Row
from bokeh.plotting import figure, curdoc
import pandas as pd
d={'created':['02/01/2019 00:00:00','03/01/2019 00:00:00','04/01/2019 00:00:00','05/01/2019 00:00:00','06/01/2019 00:00:00','07/01/2019 00:00:00'],
'aaa': [5, 4, 10, 7, 5, 5],
'bbb': [0, 10, 2, 9, 8, 4],
'ccc': [10, 12, 14, 14, 5, 7]}
df=pd.DataFrame.from_dict(d)
df['created']=pd.to_datetime(df['created'])
df.set_index('created', inplace=True)
plot=figure(plot_width=700,
plot_height=500,
x_axis_type='datetime',
title='lines')
src=ColumnDataSource(df)
products=sorted(list(src.data.keys())[1:])
product_selection=CheckboxGroup(labels=products, active =[0,1])
lines = []
for i, column in enumerate(df.columns.tolist()):
line = plot.line(x='created', y=column, source=src)
line.visible = i in product_selection.active
lines.append(line)
def update(attr, old, new):
for i, renderer in enumerate(lines):
if i in product_selection.active:
renderer.visible = True
else:
renderer.visible = False
product_selection.on_change('active', update)
curdoc().add_root(Row(plot,product_selection))
Result:

Related

ax.annotate not annotating in correct order?

I have the following code to create annotations for a plot. I basically want to plot the win ratio and the win percentage for each page type. But the annotations are labeling in an incorrect order.
Heres some reproducible code. Does anyone know why it's not in the correct order?
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
data = [['Cat page', 'No Fields', 0, 2, 0.000000],
['Cat page', 'Fields Included', 2, 2, 1.000000],
['Web page', 'No Fields', 3, 6, 0.500000],
['Web page', 'Fields Included', 3, 6, 0.500000],
['Home page', 'No Fields', 5, 13, 0.384615],
['Home page', 'Fields Included', 8, 13, 0.615385]
]
data = pd.DataFrame(data, columns = ['page_type',
'signup_field',
'win_count',
'total_wins',
'win_percent']
)
plt.figure(figsize=(8,7))
ax = sns.barplot(
x = "win_count",
y = "page_type",
hue = "signup_field",
data = data,
)
n=0
for patch in ax.patches:
w, h = patch.get_width(), patch.get_height()
w = 0 if np.isnan(w) else 1 if w == 1 else np.round(np.float(w) ,3)
y = patch.get_y()
ax.text(w , h/2+y, '({},{}/{})'.format(data.win_percent[n], data.win_count[n], data.total_wins[n]), va='center')
n+=1
Here's an image of the incorrect labeling. For example for Home page (no fields) the label should be (0.384615) 5/13 but it's giving the label for web page for some reason.

Interactive floor plan graph using Python Bokeh

I am trying to create an interactive floor-plan using Python.
Is there a way to make polygons clickable and then bring them to front in Bokeh?
In the below example:
import json
from bokeh.io import output_notebook, show
from bokeh.models import GeoJSONDataSource
from bokeh.plotting import figure, output_notebook, show
xs_dict = [
[ { 'exterior': [1, 8, 8, 1], 'holes': [] } ],
[ { 'exterior': [5, 8, 8, 5], 'holes': [] } ]
]
ys_dict = [
[ { 'exterior': [2, 2, 10, 10], 'holes': [] } ],
[ { 'exterior': [7, 7, 10, 10], 'holes': [] } ]
]
xs1 = [[[p['exterior'], *p['holes']] for p in mp] for mp in xs_dict]
ys1 = [[[p['exterior'], *p['holes']] for p in mp] for mp in ys_dict]
p = figure(plot_width=300, plot_height=300)
p.multi_polygons(xs=xs1,
ys=ys1, color=['red','green'])
This creates two polygons (outer red and inner green).
The desired behavior is when a user clicks on the green one, the green would come to front and allow user to explore that one?
Eventually, I will have more nested polygons within the green one, so a user can go to multiple depth by selecting a desired polygon. A button to go back to the previous hierarchy would be very useful as well.
If the answer to my comment to the question is "yes", then here's an example that can get you started. You can reset the zoom by clicking on the "Reset" tool button in the tool panel.
from bokeh.io import show
from bokeh.models import TapTool, CustomJS, ColumnDataSource
from bokeh.plotting import figure
base_xs = [0, 1, 1, 0]
base_ys = [0, 0, 1, 1]
def offset(base, d):
return [i + d for i in base]
ds = ColumnDataSource(dict(xs=[[[offset(base_xs, dx)]] for dx in base_xs],
ys=[[[offset(base_ys, dy)]] for dy in base_ys],
color=['red', 'green', 'blue', 'yellow']))
tt = TapTool(behavior='inspect')
p = figure(tools=[tt, 'reset'])
p.multi_polygons('xs', 'ys', color='color', source=ds)
tt.callback = CustomJS(args=dict(x_range=p.x_range, y_range=p.y_range),
code="""\
const {source} = cb_data;
const idx = source.inspected.indices[0];
const {xs, ys} = source.data;
const poly_xs = xs[idx].flat(2);
const poly_ys = ys[idx].flat(2);
x_range.start = Math.min(...poly_xs);
x_range.end = Math.max(...poly_xs);
y_range.start = Math.min(...poly_ys);
y_range.end = Math.max(...poly_ys);
""")
show(p)

Bokeh: Not able to update format of hover tooltip

I'm trying to update the format of an already defined hover tooltip, but I do not observe any change. The change I do in the example below is changing the x-axis between number and time scale ('00:00:00'). The x-axis is updated as expected. Using Bokeh version 0.12.16, mac OS X, Safari browser.
Any hints with respect to what I'm doing wrong is appreciated.
from bokeh.plotting import figure, ColumnDataSource
from bokeh.models import HoverTool, NumeralTickFormatter, AdaptiveTicker
from bokeh.models.widgets import RadioGroup
from bokeh.layouts import row, widgetbox
from bokeh.io import curdoc
def update_axis_format(new):
if new == 0:
format_num = '0'
mantissas= [1,2,5]
else:
format_num = '00:00:00'
mantissas=[3.6, 7.2, 18]
p.xaxis[0].formatter = NumeralTickFormatter(format = format_num)
p.xaxis.ticker = AdaptiveTicker(base = 10, mantissas = mantissas)
p.xgrid.ticker = AdaptiveTicker(base = 10, mantissas = mantissas)
p.tools[0].tooltips[2] = ("x", "#x{{{}}}".format(format_num))
source = ColumnDataSource(data=dict(
x=[10, 2000, 10000, 40000, 50000],
y=[2, 5, 8, 2, 7],
desc=['A', 'b', 'C', 'd', 'E'],
))
hover = HoverTool(tooltips=[
("index", "$index"),
("desc", "#desc"),
("x", "#x")
])
p = figure(plot_width=400, plot_height=400, tools=[hover],
title="Mouse over the dots")
p.circle('x', 'y', size=20, source=source)
xaxis_format = RadioGroup(
labels=["x-axis as number", "x-axis as time"], active=0)
xaxis_format.on_click(update_axis_format)
widget = widgetbox(xaxis_format)
curdoc().add_root(row(widget,p))
The BokehJS code is not sensitive to "internal" (i.e. in place) changes to tooltips. You need to replace the tooltips value entirely. E.g. this simplified code works as expected:
def update_axis_format(new):
if new == 0:
format_num = '0'
mantissas= [1,2,5]
else:
format_num = '00:00:00'
mantissas=[3.6, 7.2, 18]
p.xaxis[0].formatter = NumeralTickFormatter(format = format_num)
p.xaxis.ticker = AdaptiveTicker(base = 10, mantissas = mantissas)
p.xgrid.ticker = AdaptiveTicker(base = 10, mantissas = mantissas)
# replace all of tooltips, not just part
p.tools[0].tooltips = [("x", "#x{{{}}}".format(format_num))]
hover = HoverTool(tooltips=[("x", "#x")])

Conditional color formatting of pandas data for export to Excel [duplicate]

In Excel cell text will vary from Pass to Fail.I have to give background color green for Pass(pass/Passed/passed) and red for Fail(fail/Failed/failed) respectively. How to change the color based on text ?
My Script
import xlwt
workbook = xlwt.Workbook()
worksheet = workbook.add_sheet('Testing')
worksheet.write_merge(5, 5, 1, 1,'S.No')
worksheet.write_merge(5, 5, 2, 2,'Test Case Description')
worksheet.write_merge(5, 5, 3, 3,'Status')
worksheet.write_merge(5, 5, 4, 4,'Remarks')
worksheet.write_merge(6, 6, 1, 1,1)
worksheet.write_merge(7, 7, 1, 1,1)
worksheet.write_merge(6, 6, 2, 2,'Verify Transferring rate')
worksheet.write_merge(7, 7, 2, 2,'Verify Receiving rate')
worksheet.write_merge(6, 6, 3, 3,'Pass')
worksheet.write_merge(7, 7, 3, 3,'Fail')
workbook.save('testexcel.xls')
#Henry:
Modified code :
import xlwt
workbook = xlwt.Workbook()
worksheet = workbook.add_sheet('Status')
passed = xlwt.easyxf('back_color green')
failed = xlwt.easyxf('back_color red')
color = (passed if passorfail in ['pass','Passed','passed'] else
(failed if passorfail in ['fail','Failed','failed'] else xlwt.easyxf()))
worksheet.write_merge(6, 6, 3, 3,passorfail, style = color)
workbook.save('passfail2.xls')
print "Completed"
And it's throwing error when execute ? How to resolve this error ?
Traceback (most recent call last):
File "G:\airspan_eclipse\Excel_Gen\passfail2.py", line 5, in <module>
passed = xlwt.easyxf('back_color green')
File "C:\Python27\lib\site-packages\xlwt\Style.py", line 704, in easyxf
field_sep=field_sep, line_sep=line_sep, intro_sep=intro_sep, esc_char=esc_char, debug=debug)
File "C:\Python27\lib\site-packages\xlwt\Style.py", line 632, in _parse_strg_to_obj
raise EasyXFCallerError('line %r should have exactly 1 "%c"' % (line, intro_sep))
xlwt.Style.EasyXFCallerError: line 'back_color green' should have exactly 1 ":"
You can create styles using easyxf and then pass them as arguments to your write method.
For example:
style_pass = xlwt.easyxf('pattern: pattern solid, fore_colour green;')
style_fail = xlwt.easyxf('pattern: pattern solid, fore_colour red;')
worksheet.write_merge(6, 6, 3, 3,'Pass', style=style_pass)
worksheet.write_merge(7, 7, 3, 3,'Fail', style=style_fail)
You'll need to put in a if statement to seperate pased on pass fail.
Then, you'll use that to make a color string, something like 'fore-colour grey25'. Look in Style.py for lists of all possible colors and options (github page: https://github.com/python-excel/xlwt/blob/master/xlwt/Style.py). Since red and green both work, and back_color also works, you can do:
passed = xlwt.easyxf('back_color green')
failed = xlwt.easyxf('back_color red')
color = (passed if passorfail in ['pass','Passed','passed'] else
(failed if passorfail in ['fail','Failed','failed'] else xlwt.easyxf()))
worksheet.write_merge(6, 6, 3, 3,passorfail, style = color)

Multiline chart from dataframe using nvd3

The nvd3 line chart in the example below uses python list as data source. But how to plot multiline from a pandas dataframe without explicitly stating the columns i.e. like in pandas plot: df.plot() df could contain x columns.
from nvd3 import lineChart
# Open File for test
output_file = open('test_lineChart.html', 'w')
# ---------------------------------------
type = "lineChart"
chart = lineChart(name=type, x_is_date=False, x_axis_format="AM_PM")
xdata = list(range(0, 24))
ydata = [0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 4, 3, 3, 5, 7, 5, 3, 16, 6, 9, 15, 4, 12]
ydata2 = [9, 8, 11, 8, 3, 7, 10, 8, 6, 6, 9, 6, 5, 4, 3, 10, 0, 6, 3, 1, 0, 0, 0, 1]
kwargs1 = {'color': 'black'}
kwargs2 = {'color': 'red'}
extra_serie = {"tooltip": {"y_start": "There is ", "y_end": " calls"}}
chart.add_serie(y=ydata, x=xdata, name='sine', extra=extra_serie, **kwargs1)
extra_serie = {"tooltip": {"y_start": "", "y_end": " min"}}
chart.add_serie(y=ydata2, x=xdata, name='cose', extra=extra_serie, **kwargs2)
chart.buildhtml()
output_file.write(chart.htmlcontent)
# close Html file
output_file.close()
How to plot from this dataframe using nvd3:
df = pd.DataFrame(data)
df = df.set_index('datetime')
fig, ax = plt.subplots()
df.plot(ax=ax, marker='o')
IIUC, the chart takes the data as list, so you would have to convert your index and column data to list like so (assuming your column names are col1 and col2 respectively:
def plot_nvd3(df, ydata='col1', ydata2='col2'):
# Open File for test
output_file = open('test_lineChart.html', 'w')
# ---------------------------------------
type = "lineChart"
chart = lineChart(name=type, x_is_date=False, x_axis_format="AM_PM")
xdata = df.index.tolist()
ydata = df[ydata].tolist()
ydata2 = df[ydata2].tolist()
kwargs1 = {'color': 'black'}
kwargs2 = {'color': 'red'}
extra_serie = {"tooltip": {"y_start": "There is ", "y_end": " calls"}}
chart.add_serie(y=ydata, x=xdata, name='sine', extra=extra_serie, **kwargs1)
extra_serie = {"tooltip": {"y_start": "", "y_end": " min"}}
chart.add_serie(y=ydata2, x=xdata, name='cose', extra=extra_serie, **kwargs2)
chart.buildhtml()
output_file.write(chart.htmlcontent)
# close Html file
output_file.close()
Usage would by:
plot_nvd3(df, 'col1', 'col2')
I have not checked how nvd3 works with DateTimeIndex, though, in case your df = df.set_index('datetime') results in one.

Categories