Holoviews AdjointLayout with Bokeh Widgets - python

I am trying to append an AdjointLayout of a Scatter plot with two supporting histograms to a Bokeh dashboard. However, whenever trying to incorporate the two in a single row, the Bokeh widgets encounter display issues and the AdjointLayout never scales. Is this the current expected behavior or is here a different approach I need to take to currently accomplish this?
Minimal Example of the problem:
import numpy as np
import pandas as pd
import holoviews as hv
from bokeh.layouts import layout
from bokeh.models import Select
from bokeh.io import curdoc
renderer = hv.renderer('bokeh').instance(mode='server')
np.random.seed(10)
data = np.random.rand(100,4)
opts = {}
opts['color_index'] = 2
opts['size_index'] = 3
opts['scaling_factor'] = 50
points = hv.Points(data, vdims=['z', 'size']).opts(plot=opts)
fields = ['berry', 'cherry', 'dairy']
x = Select(title='X-Axis:', value=fields[0], options=fields)
y = Select(title='Y-Axis:', value=fields[1], options=fields)
dashboard = points + points[0.3:0.7, 0.3:0.7].hist()
app = renderer.get_plot(dashboard).state
dashboard = layout([
[[x, y], app],
])
curdoc().add_root(dashboard)
Using Bokeh 0.13.0 and Holoviews 1.10.5

Related

How to highlight a selected area in the graph with using Bokeh?

I plotted a line graphs with using bokeh on Python. I want to highlight and take the values (Max-Min-x, y coordinates) of the selected areas with "Box Select tool" like shown below. when I choose a certain section on the graph with "box select tool" the color of the selected part does not change. How to solve this problem?
Example
import numpy as np
import pandas as pd
from bokeh.plotting import figure,show,output_file
from bokeh.models import ColumnDataSource
output_file("PlottingTest.html")
dataset = pd.read_csv("data.csv")
data = dataset.iloc[:,3]
time = np.linspace(1, 500, num = 500)
TOOLS ="pan,wheel_zoom,reset,hover,poly_select,xbox_select,lasso_select"
s1 = ColumnDataSource(data=dict(x=time, y=data))
p = figure(title = 'Test',x_axis_label = 'time', y_axis_label='csv Data',plot_width=1000, plot_height=500,tools=TOOLS)
p.line ('date', 't1', source=s1, selection_color="orange")
p.line(time, data, legend_label="Current", line_width=1)
p.toolbar.autohide = True
show(p)

Can bokeh dynamically update the number of columns in a gridplot?

I have 2 bokeh rows. The top row contains a DataTable and a TextInput, both of which are able to stretch_width in order to fit the width of the browser. The bottom row contains a gridplot, which is able to stretch_width, but only does so by distorting the scale of the image. Ideally, I would like the gridplot to update the amount of columns displayed based on the size of the browser.
Consider the following example:
import pandas as pd
from bokeh.models.widgets import DataTable, TableColumn
from bokeh.models import ColumnDataSource, TextInput
from bokeh.plotting import figure, output_file, save
from bokeh.layouts import row, column, gridplot
def get_datatable():
"""this can stretch width without issue"""
df = pd.DataFrame({'a': [0, 1, 2], 'b': [2, 3, 4]})
source = ColumnDataSource(df)
Columns = [TableColumn(field=i, title=i) for i in df.columns]
data_table = DataTable(columns=Columns, source=source, sizing_mode='stretch_width', max_width=9999)
return data_table
def get_text_input():
"""this can stretch width without issue"""
return TextInput(value='Example', title='Title', sizing_mode="stretch_width", max_width=9999)
def get_gridplot():
"""
this requires columns to be hard-coded
stretch_width is an option, but only distorts the images if enabled
"""
figs = []
for _ in range(30):
fig = figure(x_range=(0,10), y_range=(0,10))
_ = fig.image_rgba(image=[], x=0, y=0)
figs.append(fig)
return gridplot(children=figs, ncols=2)
top_row = row([get_datatable(), get_text_input()], max_width=9999, sizing_mode='stretch_width')
bottom_row = row(get_gridplot())
col = column(top_row, bottom_row, sizing_mode="stretch_width")
output_file("example.html")
save(col)
My end goal is to have the gridplot automatically update the amount of columns based on the width of the browser. Is there a way to do this natively in bokeh? If not, is it possible to do this via a CustomJs javascript callback?
Solution
Consider using sizing_mode=“scale_width” when calling figure.
fig = figure(x_range=(0,10), y_range=(0,10), sizing_mode=“scale_width”)
Note
It may be preferable to use scale_width instead of stretch_width more generally.
Bokeh Doc Example: https://docs.bokeh.org/en/latest/docs/user_guide/layout.html#multiple-objects

Incomplete bokeh plot

I have a pandas dataframe of 10 columns and trying to get bar plot using Bokeh.
The HTML file has the complete plot when I use plot_width=10000.
However when I increase the plot width(so that there is space between x axes values) to 30000, the plot does not fill beyond 2010. Here is the complete code. Please suggest the way forward.
from bokeh.palettes import Viridis6 as palette
from bokeh.transform import factor_cmap
from bokeh.models import ColumnDataSource,FactorRange,HoverTool
from bokeh.palettes import Spectral6
from flask import Flask, request, render_template, session, redirect,send_file
import numpy as np
import pandas as pd
from bokeh.plotting import figure, show, output_file,save
from bokeh.embed import components,file_html
from bokeh.resources import CDN
from bokeh.layouts import row,column
from bokeh.core.properties import value
dates = pd.date_range('20050101', periods=3900)
df = pd.DataFrame(np.random.randn(3900, 10), index=dates, columns=list('ABCDEFGHIJ'))
s = df.resample('M').mean().stack()
s.index = [s.index.get_level_values(0).strftime('%Y-%m-%d'),s.index.get_level_values(1)]
x = s.index.values
l1=list(s.index.levels[1])
counts = s.values
source = ColumnDataSource(data=dict(x=x, counts=counts))
p = figure(x_range=FactorRange(*x), plot_height=250,plot_width=30000, title='Plotting data',
toolbar_location=None, tools="")
p.vbar(x='x', top='counts', width=1, source=source, line_color="white")
p.y_range.start = s.values.min()
p.y_range.end = s.values.max()
p.x_range.range_padding = 0.01
p.y_range.range_padding = 0.01
p.xaxis.major_label_orientation = 1
p.xgrid.grid_line_color = None
output_file('test_plot.html')
save([p])
show(p)
This works fine for me with Bokeh 1.0.4 and OSX/Safari. I suspect this is a limitation/issue with the underlying HTML Canvas implementation in whatever browser you are using, in which case there is nothing we can do about it. The only suggestions I can make are to split the plot up into smaller subplots, or use a different browser (or possibly different version of the same browser)

Python Bokeh: Slider callback in ColumnDataSource not update

I having a problem with the callback, I got everything worked expect the part when the graph doesn't update even thou the array is updated when I change the slider.
import numpy as np
from bokeh.io import curdoc
from bokeh.layouts import row, widgetbox
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
data = {'x_values': [0,0,2,2,4,4],
'y_values': [10,0,0,5,5,10]} #Seting up data
source = ColumnDataSource(data=data) # Map plot
plot = figure(title="Step Well",
tools="save,wheel_zoom")
plot.line('x_values', 'y_values',source=source)
def update_data(attrname, old, new):
Step = StepHeight.value
x = [0,0,2,2,4,4]
y = [10,0,0,Step,Step,10]
source.data = ColumnDataSource(dict(x=x, y=y))
source.on_change('value', update_data)
StepHeight = Slider(title="Step Height",
value=4.0,
start=2.0, end=6.0, step=0.2)
# Set up layouts and add to document
inputs = widgetbox(StepHeight)
layout = row(inputs, plot)
curdoc().title = "Sliders"
curdoc().add_root(layout)
You were trying to make source.data a column data source but source should be the column data source. Source.data is just a dictionary. I changed some things in your code and it should work fine now.
import numpy as np
from bokeh.io import curdoc
from bokeh.layouts import row, widgetbox
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
data = {'x_values': [0,0,2,2,4,4],
'y_values': [10,0,0,5,5,10]} #Seting up data
source = ColumnDataSource(data=data) # Map plot
plot = figure(title="Step Well",
tools="save,wheel_zoom")
plot.line('x_values', 'y_values',source=source)
def update_data(attrname, old, new):
y = [10,0,0,new,new,10]
source.data['y_values'] = y
StepHeight = Slider(title="Step Height",
value=4.0,
start=2.0, end=6.0, step=0.2)
StepHeight.on_change('value', update_data)
# Set up layouts and add to document
inputs = widgetbox(StepHeight)
layout = row(inputs, plot)
curdoc().title = "Sliders"
curdoc().add_root(layout)

Add text annotations to each data point in Holoviews plot

In Bokeh I am able to add a text annotation to each point in my plot programmatically by using LabelSet. Below I give an example for a simple bar plot:
import numpy as np
import pandas as pd
# Make some data
dat = \
(pd.DataFrame({'team':['a','b','c'], 'n_people':[10,5,12]})
.assign(n_people_percent = lambda x: (x['n_people']/np.sum(x['n_people'])*100)
.round(1).astype('string') + '%')
)
dat
# Bar plot with text annotations for every bar
from bkcharts import show, Bar
from bkcharts.attributes import CatAttr
from bokeh.models import (ColumnDataSource, LabelSet)
source_labs = ColumnDataSource(data = dat)
p = Bar(data = dat, label = CatAttr(columns = 'team'), values = 'n_people')
labels = LabelSet(x = 'team', y = 'n_people',
text = 'n_people_percent', source = source_labs)
p.add_layout(labels)
show(p)
However I am not sure how to achieve the same thing with Holoviews. I can make the same bar plot without the annotations very easily:
import holoviews as hv
hv.extension('bokeh')
p = hv.Bars(dat, kdims=['team'], vdims=['n_people'])
p
I can add a single text label adding an Overlay with the hv.Text element
p * hv.Text('a', 11, '37.0%')
But I have no idea how I can label each bar without explicitly calling hv.Text separately for every data point (bar). The problem seems to be that hv.Text does not accept a data argument like other elements e.g. hv.Bars, instead just x and y coordinates. My intuition would be that I should be able to do something like
p * hv.Text(dat, kdims=['team'], vdims=['n_people_percent'])
Any help with this appreciated!
Looks like this commit adds vectorized labels to hv.Labels, so try:
import holoviews as hv
hv.extension('bokeh')
p = hv.Bars(dat, kdims=['team'], vdims=['n_people'])
p * hv.Labels(dat, kdims=['team', 'n_people'], vdims=['n_people_percent'])

Categories

Resources