Holoviews Bars Ignoring .opts() - python

I am trying to pass .opts() arguments to style a holoviews .Bars graph, but all arguments are being ignored when passed. This results in the following error:
WARNING:param.Bars02978: Use of call to set options will be deprecated in the next major release (1.14.0). Use the equivalent .opts method instead.
I am on version 1.14.8.
How can I pass the .opts() so my graph can be styled appropriately?
# https://www.youtube.com/watch?v=ZWP_h6WV8h0 Connecting multiple plots
from collections import defaultdict
import panel as pn
import holoviews as hv
from holoviews import opts
import pandas as pd
import numpy as np
import pandas_bokeh
import bokeh
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, TableColumn, DataTable, DateEditor, IntEditor\
, DateFormatter, NumeralTickFormatter, CustomJS, DatePicker, widgets
from bokeh.models import Panel, Tabs, Row
from bokeh.io import show
from bokeh.layouts import Column, layout
import hvplot.pandas
hv.extension('bokeh', 'matplotlib')
renderer = hv.renderer('bokeh')
renderer = renderer.instance(mode='server')
data = {
"calories": [420],
"duration": [50],
"ggg": [60]
}
#load data into a DataFrame object:
df = pd.DataFrame(data)
print(df)
df2 = pd.DataFrame({'Missing Data': df[['calories']].sum(axis=1), 'Delayed Data': df[['duration']].sum(axis=1)
, 'Duplicate Data': df[['ggg']].sum(axis=1)})
print(df2)
bar_opts = opts.Bars(height=400, width=600, tools=["hover"], bgcolor="grey", xlabel="Wine Class", ylabel="Malic Acid", ylim=(0.0, 3.5))
bars = hv.Bars(pd.melt(df2.reset_index(), ['index']), ['index', 'variable'], 'value', label='dl_refined_analytics').opts(bar_opts)
bars.opts(height=400, width=600, tools=["hover"], bgcolor="grey", xlabel="Wine Class", ylabel="Malic Acid", ylim=(0.0, 3.5))
dmap = hv.DynamicMap(bars)
app = pn.Row(dmap)
app.show() ```

What was the intent behind hv.DynamicMap(bars)? I'm not sure why you'd want to pass the bars object to a DynamicMap constructor, and if you just use app = pn.Row(bars) instead of app = pn.Row(dmap) it should work fine.

Related

How to update a python bokeh plot as soon as data is available? [duplicate]

I want Bokeh to update periodically and arbitrarily when the results from a separate algorithm running in python returns results, not based on any input from the Bokeh interface.
I've tried various solutions but they all depend on a callback to a some UI event or a periodic callback as in the code below.
import numpy as np
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, Plot, LinearAxis, Grid
from bokeh.models.glyphs import MultiLine
from time import sleep
from random import randint
def getData(): # simulate data acquisition
# run slow algorith
sleep(randint(2,7)) #simulate slowness of algorithm
return dict(xs=np.random.rand(50, 2).tolist(), ys=np.random.rand(50, 2).tolist())
# init plot
source = ColumnDataSource(data=getData())
plot = Plot(
title=None, plot_width=600, plot_height=600,
min_border=0, toolbar_location=None)
glyph = MultiLine(xs="xs", ys="ys", line_color="#8073ac", line_width=0.1)
plot.add_glyph(source, glyph)
xaxis = LinearAxis()
plot.add_layout(xaxis, 'below')
yaxis = LinearAxis()
plot.add_layout(yaxis, 'left')
plot.add_layout(Grid(dimension=0, ticker=xaxis.ticker))
plot.add_layout(Grid(dimension=1, ticker=yaxis.ticker))
curdoc().add_root(plot)
# update plot
def update():
bokeh_source = getData()
source.stream(bokeh_source, rollover=50)
curdoc().add_periodic_callback(update, 100)
This does seem to work, but is this the best way to go about things? Rather than having Bokeh try to update every 100 milliseconds can I just push new data to it when it becomes available?
Thanks
You can use zmq and asyncio to do it. Here is the code for the bokeh server, it wait data in an async coroutine:
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure, curdoc
from functools import partial
from tornado.ioloop import IOLoop
import zmq.asyncio
doc = curdoc()
context = zmq.asyncio.Context.instance()
socket = context.socket(zmq.SUB)
socket.connect("tcp://127.0.0.1:1234")
socket.setsockopt(zmq.SUBSCRIBE, b"")
def update(new_data):
source.stream(new_data, rollover=50)
async def loop():
while True:
new_data = await socket.recv_pyobj()
doc.add_next_tick_callback(partial(update, new_data))
source = ColumnDataSource(data=dict(x=[0], y=[0]))
plot = figure(height=300)
plot.line(x='x', y='y', source=source)
doc.add_root(plot)
IOLoop.current().spawn_callback(loop)
to send the data just run following code in another python process:
import time
import random
import zmq
context = zmq.Context.instance()
pub_socket = context.socket(zmq.PUB)
pub_socket.bind("tcp://127.0.0.1:1234")
t = 0
y = 0
while True:
time.sleep(1.0)
t += 1
y += random.normalvariate(0, 1)
pub_socket.send_pyobj(dict(x=[t], y=[y]))

Bokeh attribute update doesn't update Model without removing a model from curdoc

The below code is a minimal example of an issues where a bokeh model doesn't update when it's attribute is set via a callback. I've found that removing and adding back a model object (not even the suspect one) from the layout of the curdoc forces it to refresh. I've shown this via the first button press.
Is there a more elegant way to force bokeh to redraw the figure?
The example is for DataTable.columns.formatter, but I've noticed that this applies to other model attributes as well (including axis ranges, where I've seen a workaround involving setting the range explicitly at figure creation to allow updates).
from bokeh.models.widgets import Dropdown, RadioButtonGroup, CheckboxGroup, \
Toggle, DataTable, TableColumn, NumberFormatter
from bokeh.plotting import figure, curdoc, ColumnDataSource
from bokeh.layouts import column, layout
def update_format(attr, old, new):
if toggle_commas.active == 1:
(t.columns[1].formatter)
# remove the commas
t.columns[1].formatter = NumberFormatter(format='0,0.[00]')
# show that it updates the actual attribute
print(t.columns[1].formatter)
del doc_layout.children[-1]
doc_layout.children.insert(1, toggle_commas)
else:
# change the formatter back and note that it doesn't update the table unless you remove and add something
(t.columns[1].formatter)
# remove the commas
t.columns[1].formatter = NumberFormatter(format='0.[00]')
# show that it updates the actual attribute
print(t.columns[1].formatter)
table_data = dict(
percentiles=['min', '1st', '5th', '10th', '25th', '50th',
'75th', '90th', '95th', '99th', 'max', '', 'mean', 'std'],
values=[i for i in range(1000, 1014)]
)
table_source = ColumnDataSource(table_data)
table_columns = [
TableColumn(field="percentiles", title="Percentile"),
TableColumn(field="values", title="Value", formatter=NumberFormatter(format='0.[00]'))
]
t = DataTable(source=table_source, columns=table_columns, width=400, height=600,
name='pct_table')
toggle_commas = Toggle(label='Commas', active=False)
toggle_commas.on_change('active', update_format)
doc_layout = layout(t, toggle_commas)
curdoc().add_root(doc_layout)

Select columns (not rows) in Bokeh Table Widget?

I have a bokeh table that is linked to a plot, and is working as intended. Selecting a row in the table mutes all the non-selected rows in the plot display.
However, what if someone wants to select a column, and hide all other columns in the plot? Is this possible using a bokeh widget? Or does some custom code need to be written for this feature? I have attached to code used to produce the widget table on the bokeh website, as it is the most simple example I can think of (and quickest).
from datetime import date
from random import randint
from bokeh.io import output_file, show
from bokeh.layouts import widgetbox
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import DataTable, DateFormatter, TableColumn
output_file("data_table.html")
data = dict(
dates=[date(2014, 3, i+1) for i in range(10)],
downloads=[randint(0, 100) for i in range(10)],
)
source = ColumnDataSource(data)
columns = [
TableColumn(field="dates", title="Date", formatter=DateFormatter()),
TableColumn(field="downloads", title="Downloads"),
]
data_table = DataTable(source=source, columns=columns, width=400, height=280)
show(widgetbox(data_table))
Here is a code with a JS callback that allows you to know the selected row and column.
from bokeh.io import show
from bokeh.layouts import widgetbox
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.widgets import DataTable,TableColumn
column_list = ['col1','col2','col3']
source = ColumnDataSource(data = {key:range(10) for key in column_list})
columns = [TableColumn(field=col, title=col) for col in column_list]
data_table = DataTable(source=source, columns=columns, width=400, height=280,selectable=True)
source_code = """
var grid = document.getElementsByClassName('grid-canvas')[0].children;
var row = '';
var col = '';
for (var i=0,max=grid.length;i<max;i++){
if (grid[i].outerHTML.includes('active')){
row=i;
for (var j=0,jmax=grid[i].children.length;j<jmax;j++){
if(grid[i].children[j].outerHTML.includes('active')){col=j}
}
}
}
console.log('row',row);
console.log('col',col);
cb_obj.selected['1d'].indices = [];
"""
source.callback = CustomJS(code= source_code)
show(widgetbox(data_table))
The line cb_obj.selected['1d'].indices = []; just resets the selected indices so that the callback can trigger even if the same cell is clicked multiple times
You can then do what you want with the row/column index
If you need to you can also "transfer" the values back to python by updating a ColumnDatasource with the row and column values.
I use bokeh 0.12.10 so this may require some change with the latest version
EDIT: tested with 0.12.16 and it still works
EDIT: update for bokeh 1.1.0
from bokeh.io import show
from bokeh.layouts import widgetbox
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.widgets import DataTable,TableColumn
column_list = ['col1','col2','col3']
source = ColumnDataSource(data = {key:range(20) for key in column_list})
columns = [TableColumn(field=col, title=col) for col in column_list]
data_table = DataTable(source=source, columns=columns, width=400, height=280,selectable=True)
source_code = """
var grid = document.getElementsByClassName('grid-canvas')[0];
var active_row = grid.querySelectorAll('.active')[0];
if (active_row!=undefined){
var active_row_ID = Number(active_row.children[0].innerText);
for (var i=1, imax=active_row.children.length; i<imax; i++){
if (active_row.children[i].className.includes('active')){
var active_col_ID = i-1;
}
}
console.log('row',active_row_ID);
console.log('col',active_col_ID);
var active_cells = grid.querySelectorAll('.active');
for (i=0, imax=active_cells.length;i<imax;i++){
active_cells[i].classList.remove('active');
}
cb_obj.indices = [];
}
"""
source.selected.js_on_change('indices', CustomJS(args={'source':source},code= source_code) )
show(widgetbox(data_table))
As of Bokeh 0.12.16 the built-in DataTable does not support any sort of column selection or click events. Looking at the descriptions of Grid Events for the underlying SlickGrid implementation, it appears that onClick and onHeaderClick are supported at the low level. So the immediately accessible option would be Extending Bokeh with a custom DataTable subclass. Otherwise you can submit a GitHub feature request issue to discuss exposing these events somehow in the built in table.

ipywidgets graph widget implementation

Good day
I have been on this all day. I am trying to add widgets to my notebooks on my Jupyter(ipython) notebook. Below is my code which makes sense in my head but it is not populating anything. I am trying to control the list of values in StatusesList through a widget so it changes the graph populated. Please help.
from plotly import __version__
from plotly.offline import download_plotlyjs, init_notebook_mode, iplot
import plotly.plotly as py
import plotly.graph_objs as go
from plotly.graph_objs import *
import numpy as np
import cufflinks as cf
init_notebook_mode()
cf.go_offline()
import pandas as pd
cf.set_config_file(theme='pearl')
from __future__ import print_function
from ipywidgets import interact, interactive, fixed
import pandas as pd
import ipywidgets as widgets
from IPython.display import display
df = pd.read_excel("C:\Users\UserName\Downloads\Complaints Management.xlsx")
df_status = df[['Status', 'Member Number']].groupby('Status').count()
StatusesList = df_status.index.tolist()
GragphValueList = df_status["Member Number"].tolist()
# w = Output()
def f(x):
x = StatusesList
return x
interact(f, x = StatusesList)
fig_status = {
'data': [{'labels': x,
'values': GragphValueList,
'type': 'pie'}],
'layout': {'title': 'Complaints by status'}
}
iplot(fig_status)
file sample
I think you are looking for this type of interactivity right?
by_list = ['Priority', 'Status', 'Deadline', 'Query Category'] #df.keys().tolist()
#interact(x = by_list )
def f(x):
df_data = df[[x, 'Member Number']].groupby(x).count()
fig_status = {
'data': [{'labels': df_data.index.tolist(),
'values': df_data["Member Number"].tolist(),
'type': 'pie'}],
'layout': {'title': 'Complaints by %s' % x}
}
iplot(fig_status)
You have control over the by_list by adding or removing string corresponding to fields in your Excel. It is also possible to get all fields using df.keys().tolist() but this might be a little unstable.
PS. Thank you for showing me plotly, I hadn't heard/seen it before but it can produce some nice (interactive) statistical graphs!

How do I work with images in Bokeh (Python)

For example you can plot an image in matplotlib using this code:
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
img=mpimg.imread('image.png')
plt.imshow(img)
Is something like this possible with Bokeh(0.10)?
You can use the ImageURL glyph (image_url plot method)to load images locally or from the web.
from bokeh.plotting import figure, show, output_file
output_file('image.html')
p = figure(x_range=(0,1), y_range=(0,1))
p.image_url(url=['tree.png'], x=0, y=1, w=0.8, h=0.6)
## could also leave out keywords
# p.image_url(['tree.png'], 0, 1, 0.8, h=0.6)
show(p)
One gotcha - if you graph only an image (and no other data), you'll have to explicitly set the plot ranges.
Here's the docs:
http://docs.bokeh.org/en/latest/docs/reference/models/glyphs.html#bokeh.models.glyphs.ImageURL
The earlier answer was helpful. However, I wanted an image only option without any additional object. So, adding the answer for Bokeh version 0.12.0 and removed all the grids, axes and toolbar.
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, Range1d
bosch_logo = "static/tree.jpg"
logo_src = ColumnDataSource(dict(url = [bosch_logo]))
page_logo = figure(plot_width = 500, plot_height = 500, title="")
page_logo.toolbar.logo = None
page_logo.toolbar_location = None
page_logo.x_range=Range1d(start=0, end=1)
page_logo.y_range=Range1d(start=0, end=1)
page_logo.xaxis.visible = None
page_logo.yaxis.visible = None
page_logo.xgrid.grid_line_color = None
page_logo.ygrid.grid_line_color = None
page_logo.image_url(url='url', x=0.05, y = 0.85, h=0.7, w=0.9, source=logo_src)
page_logo.outline_line_alpha = 0
curdoc().add_root(page_logo)
Another option is to display the image in a div.:
from bokeh.io import output_notebook, show
from bokeh.models.widgets import Div
output_notebook()
div_image = Div(text="""<img src="https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/7.png" alt="div_image">""", width=150, height=150)
show(div_image)
ImageURL can't get updated dynamically with a callback. However, using a div, you can do so by treating the div_image.text as a regular Python string, for example:
from ipywidgets import interact
from bokeh.io import output_notebook, show, push_notebook
from bokeh.models.widgets import Div
output_notebook()
div_image = Div(text="""<img src="https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png" alt="div_image">""", width=100, height=100)
def update(pokemon_number=1):
div_image.text = """<img src="https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/{}.png" alt="div_image">""".format(pokemon_number)
push_notebook()
show(div_image, notebook_handle=True)
interact(update, pokemon_number=[1, 4, 7])
Of course, the image source can also point to a local file.
(Tested in Python 3.7.3 and bokeh 1.2.0)
Running this example using bokeh serve is a bit more tricky. I suggest to setup working directory properly:
server_folder/
+main.py
+static/
+logo.png
.. and run bokeh serve command from directory ABOVE server_folder
bokeh serve server_folder --show
Then this code works for me
#main.py file
from bokeh.plotting import figure, curdoc
x_range = (-20,-10) # could be anything - e.g.(0,1)
y_range = (20,30)
p = figure(x_range=x_range, y_range=y_range)
#img_path = 'https://docs.bokeh.org/en/latest/_static/images/logo.png'
img_path = 'server_folder/static/logo.png'
p.image_url(url=[img_path],x=x_range[0],y=y_range[1],w=x_range[1]-x_range[0],h=y_range[1]-y_range[0])
doc = curdoc()
doc.add_root(p)
Here is a simple example that works, almost the same simple format as you requested:
from PIL import Image
import numpy as np
from bokeh.plotting import figure, output_notebook, show
output_notebook()
#load image
im = Image.open('Segment_image.png') # just replace any image that you want here
p = figure()
imarray = np.array(im.convert("RGBA"))
plotted_image = p.image_rgba(image=[imarray.view("uint32").reshape(imarray.shape[:2])], x=0, y=0, dw=imarray.shape[0], dh=imarray.shape[1])
show(p)

Categories