Bokeh Figure won't Update? - python

I'm new to Bokeh and was wondering if anyone could lend a little help tell me why my plot is not updating? The code is very simple, and can be found here:
http://pastebin.com/MLAigEG6
The code is just supposed to grab some data using the function "get_dataset", plot a bar chart, and let me update the plot using a dropdown box and slider. The two small dataframes can be found here:
https://github.com/degravek/bdata
The slider is set to default at 15 (30 total values plotted). If the slider is moved, or if the dropdown box is changed, the axes for the plot don't update for some reason. For example, if the slider is set to 2, there should only be 2 bars shown, and the axes should adjust accordingly. Thanks a lot for taking a look.

Nice code. In your update function, you also need to update the x_range.factors of the plot. And global asdata is not needed here.
def update_samples_or_dataset(attrname, old, new):
dataset = dataset_select.value
n_samples = int(samples_slider.value)
asdata = get_dataset(dataset, n_samples)
plot.x_range.factors = asdata['aspects'].tolist() # this was missing
source.data = dict(x=asdata['aspects'].tolist(), y=asdata['importance'].values)

Related

How can i change existing Bokeh plot tick sizes?

How can i change the existing Bokeh plot ticks size, specifically package that was being used to plot already defined a plot layout of bokeh ? How can i overwrite those layout ? Below is the example code of the plot to reproduce the plot.
# !pip install arlpy
import arlpy.uwapm as pm
import arlpy.plot as plt
env = pm.create_env2d()
pm.plot_env(env, width=900)
Here is the generated figure for which i want to redefined layout.
This is a bit tricky and but it is possible to get it done. But I have to mention that I don't like this solution.
To get a figure you have to enable the hold argument because otherwise it will be plotted immediately and return None. Then you can get the current figure using arlpy.plot.gcf() and set all parameters. To show the figure now you have to use the bokeh call show because the figure is a bokeh object now and in arlpy it is not available to set the hold to False again and show the figure. In fact the figure is now waiting for some data to add.
Here is an example.
from bokeh.plotting import figure, show
import arlpy.plot
arlpy.plot.figure(title='Demo 1', width=500)
arlpy.plot.plot([0,10], [0,10], hold=True)
p = arlpy.plot.gcf()
p.axis.major_tick_line_width = 3
show(p)
Unfortunately I found no solution where I use import arlpy.uwapm as pm because there is no return parameter in the plot_envfunction. So you can`t get the figure and make some changes.
I hope this helps. But I guess it is not satisfying.

Bokeh Select Widget to Update Plot

I am trying to build a grid plot that updates based on value selected from 'Select' widget using Bokeh.
The graph works but there is no interaction between the widget and the graph. I am not sure how to do this. The goal is to use the 'Select' to update dfPlot then follow the remaining steps.
Here is what i have so far:
output_file('layout.html')
select = Select(title="Option:", options= list(dfExpense['Ident'].unique()), value= "VALUE")
def update_plot(attr, old, new):
dfPlot = dfExpense[dfExpense['Ident'] == select.value]
select.on_change('value', update_plot)
d = []
for x in dfPlot['Account'].unique():
d.append(f's_{x}')
plt = []
for i, x in enumerate(dfPlot['Account'].unique()):
dftemp = dfPlot[dfPlot['Account']==gl]
source1 = ColumnDataSource(dftemp)
d[i] = figure(plot_width = 250, plot_height = 250)
d[i].circle('X', 'Amount', source = source1)
plt.append(d[i])
grid= gridplot([i for i in plt], ncols = 6)
l = row(grid, select)
show(l)
curdoc().add_root(l)
Thanks!
Someone else will probably give you a better answer. I'll just say, I think you might be doing things completely wrong for what you are trying to do (I did the same thing when starting to work with Bokeh).
My understanding after a bit of experience with Bokeh, as it relates to your problem, is as follows:
Using curdoc to make an interactive widget based Bokeh plot means you are using Python to interact with the plot, meaning that you must use a Bokeh server, not just use a .html file. (as a corollary, you won't be using show or output file) https://docs.bokeh.org/en/latest/docs/user_guide/server.html
You can still make a standalone .html file and make it have interactive widgets like sliders, but you will have to write some Javascript. You'll most likely want to do this by utilizing CustomJS within Bokeh, which makes it relatively easy.
https://docs.bokeh.org/en/latest/docs/user_guide/interaction/callbacks.html
I had a similar problem, wanting interactivity without using a Python Bokeh server. CustomJS ended up serving my needs quite well, and even though I'm a novice at Javascript, they make it pretty easy (well, especially if your problem is similar to the examples, it can get tricky otherwise but still not very hard).

Python - update plot based on a timer

I have a list of 2D arrays that I plot them using a slider from matplotlib.
I added some control buttons to my plot, so I want to click a play button, and the plot changes with some time interval.
Here's the function that is called by the button:
def play(mouse_event):
for x in range(len(listOfMoments)):
image.set_data(listOfMoments[x])
time.sleep(0.5)
print(x)
The print(x) shows that x is increasing normally, however, it plots only the last array of the list, after the increment finishes.
My question is: How can I make it plot the array one by one, as it expected to be?
I should mention also that I tried the while loop and got the same results.
You need to tell the backend to update the figure after changing the data each time through the loop:
fig.canvas.draw()
Here's the documentation reference.
If you don't have a reference to the Figure object, you can probably retrieve it with fig = plt.gcf().

Show text annotations on selection in Bokeh

I have a little Bokeh plot with data points and associated text labels. What I want is for the text labels to only appear when the user selects points with the box select tool. This gets me close:
from bokeh.plotting import ColumnDataSource,figure,show
source = ColumnDataSource(
data=dict(
x=test[:,0],
y=test[:,1],
label=[unquote_plus(vocab_idx[i]) for i in range(len(test))]))
TOOLS="box_zoom,pan,reset,box_select"
p = figure(plot_width=400, plot_height=400,tools=TOOLS)
p.circle(x='x',y='y', size=10, color="red", alpha=0.25,source=source)
renderer = p.text(x='x',y='y',text='label',source=source)
renderer.nonselection_glyph.text_alpha=0.
show(p)
This is close, in that if I draw a box around some points, those text labels are shown and the rest are hidden, but the problem is that it renders all the text labels to start (which is not what I want). The initial plot should have all labels hidden, and they should only appear upon a box_select.
I thought I could start by rendering everything with alpha=0.0, and then setting a selection_glyph parameter, like this:
...
renderer = p.text(x='x',y='y',text='label',source=source,alpha=0.)
renderer.nonselection_glyph.text_alpha=0.
renderer.selection_glyph.text_alpha=1.
...
But this throws an error:
AttributeError: 'NoneType' object has no attribute 'text_alpha'
When trying to access the text_alpha attribute of selection_glyph.
I know I could use a hover effect here or similar, but need the labels to default to not being visible. An alternative, but not ideal, solution would be to have a toggle button that switches the labels on and off, but I'm not sure how to do that either.
What am I doing wrong here?
As of version 0.11.1, the value of selection_glyph is None by default. This is interpreted by BokehJS as "don't do anything different, just draw the glyph as normal". So you need to actually create a selection_glyph. There are two ways to do this, both demonstrated here:
http://docs.bokeh.org/en/latest/docs/user_guide/styling.html#selected-and-unselected-glyphs
Basically, they are
by hand
Create an actual Circle Bokeh model, something like:
selected_circle = Circle(fill_alpha=1, fill_color="firebrick", line_color=None)
renderer.selection_glyph = selected_circle
OR
using glyph method parameters
Alternatively, as a convenience Figure.circle accepts paramters like selection_fill_alpha or selection_color (basically any line or fill or text property, prefixed with selection_) :
p.circle(..., selection_color="firebrick")
Then a Circle will be created automatically and used for renderer.selection_glyph
I hope this is useful information. If so, it highlights that there are two possible ways that the project could be improved:
updating the docs to be explicit and highlight that renderer.selection_glyph is None by default
changing code so that renderer.selection_glyph is just a copy of renderer.glyph by default (then your original code would work)
Either would be small in scope and ideal for a new contributor. If you would be interested in working up a Pull Request to do either of these tasks, we (and other users) would certainly be grateful for the contribution. In which case, please just make an issue first at
https://github.com/bokeh/bokeh/issues
that references this discussion, and we can provide more details or answer any questions.

Update/Refresh matplotlib plots on second monitor

At the moment I am working with Spyder and doing my plotting with matplotlib. I have two monitors, one for development and another for (data) browsing and other stuff. Since I am doing some calculations and my code often changes, I often (re)execute the code and have a look at the plots to check if the results are valid.
Is there any way to place my matplotlib plots on a second monitor and refresh them from the main monitor?
I have already searched for a solution but could not find anything. It would be really helpful for me!
Here's some additional information:
OS: Ubuntu 14.04 (64 Bit)
Spyder-Version: 2.3.2
Matplotlib-Version: 1.3.1.-1.4.2.
I know it's an old question but I came across a similar problem and found this question. I managed to move my plots to a second display using the QT4Agg backend.
import matplotlib.pyplot as plt
plt.switch_backend('QT4Agg')
# a little hack to get screen size; from here [1]
mgr = plt.get_current_fig_manager()
mgr.full_screen_toggle()
py = mgr.canvas.height()
px = mgr.canvas.width()
mgr.window.close()
# hack end
x = [i for i in range(0,10)]
plt.figure()
plt.plot(x)
figManager = plt.get_current_fig_manager()
# if px=0, plot will display on 1st screen
figManager.window.move(px, 0)
figManager.window.showMaximized()
figManager.window.setFocus()
plt.show()
[1] answer from #divenex: How do you set the absolute position of figure windows with matplotlib?
This has to do with matplotlib, not Spyder. Placing the location of a figure explicitly appears to be one of those things for which there's really just workarounds ... see the answers to the question here. That's an old question, but I'm not sure there's been change since then (any matplotlib devs, feel free to correct me!).
The second monitor shouldn't make any difference, it sounds like the issue is just that the figure is being replaced with a new one.
Fortunately you can update figures you've moved to where you want them pretty easily, by using the object interface specifically, and updating the Axes object without creating a new figure. An example is below:
import matplotlib.pyplot as plt
import numpy as np
# Create the figure and axes, keeping the object references
fig = plt.figure()
ax = fig.add_subplot(111)
p, = ax.plot(np.linspace(0,1))
# First display
plt.show()
# Some time to let you look at the result and move/resize the figure
plt.pause(3)
# Replace the contents of the Axes without making a new window
ax.cla()
p, = ax.plot(2*np.linspace(0,1)**2)
# Since the figure is shown already, use draw() to update the display
plt.draw()
plt.pause(3)
# Or you can get really fancy and simply replace the data in the plot
p.set_data(np.linspace(-1,1), 10*np.linspace(-1,1)**3)
ax.set_xlim(-1,1)
ax.set_ylim(-1,1)
plt.draw()

Categories

Resources