Possible update in bokeh is causing a strange generator bug - python

I had the following code snippet working:
import numpy as np
import bokeh.plotting as bp
from bokeh.models import HoverTool
bp.output_file('test.html')
fig = bp.figure(tools="reset,hover")
x = np.linspace(0,2*np.pi)
y1 = np.sin(x)
y2 = np.cos(x)
s1 = fig.scatter(x=x,y=y1,color='#0000ff',size=10,legend='sine')
s1.select(dict(type=HoverTool)).tooltips = {"x":"$x", "y":"$y"}
s2 = fig.scatter(x=x,y=y2,color='#ff0000',size=10,legend='cosine')
fig.select(dict(type=HoverTool)).tooltips = {"x":"$x", "y":"$y"}
bp.show()
no the liine s1.select ... returns a generator and gives me the following bug:
AttributeError: 'generator' object has no attribute 'tooltips'
A server update took place for the process that is running this code. It is possible that bokeh may have been updated. Whats my fastest workaround this ?? or is there a bug I am missing ?

Some time ago the glyph methods were changed to return the glyph renderer, instead of the plot. This makes configuring the visual properties of the glyph renderer much easier. Returning the plot was redundant, since a user typically already has a reference to the plot. But you want to search the plot for a hover tool, not the glyph renderer, so you need to do:
fig.select(HoverTool).tooltips = {"x":"$x", "y":"$y"}
Note that using a dictionary means there is no guarantee about the order of the tooltips. If you care about the order, you should use a list of tuples:
fig.select(HoverTool).tooltips = [("x", "$x"), ("y", "$y")]
Then the tooltip rows will show up in the same order as given, top to bottom.

Related

How to set starting zoom level in EsriImagery with datashader and bokeh?

I want to project a map with its starting position like this
The current output that I get is like this
import holoviews as hv
from geoviews.tile_sources import EsriImagery
from holoviews.operation.datashader import datashade, dynspread
import datashader as ds
import colorcet as cc
hv.extension('bokeh', 'matplotlib')
c = df.loc[(df['dropoff_latitude'] >= 40.5) &
(df['dropoff_latitude'] <= 41) &
(df['dropoff_longitude'] >= -74.1) &
(df['dropoff_longitude'] <= -73.7)]
map_tiles = EsriImagery().opts(alpha=0.5, width=900, height=480, bgcolor='black')
points = hv.Points(ds.utils.lnglat_to_meters(c['dropoff_longitude'], c['dropoff_latitude']))
taxi_trips = datashade(points, dynamic = True, x_sampling=0.1, y_sampling=0.1, cmap=cc.fire, height=1000, width=1000)
map_tiles * taxi_trips
I tried to set a zoom_level or xrange, yrange in EsriImagery opts, but there are no such parameters. The method itself also has no documentation. And I couldn't find the documentation regrading this online too. (I could be looking at the wrong place.)
There are two ways to do this:
Option 1 -- dircet input
Set your wanted values using the parameter x_range and y_range in datashade(...).
taxi_trips = datashade(points, x_range=(-8250000,-8200000))
Option 2 -- indirect input
If you don't know the needed values and you want to play around a bit, you can use this workaround.
The existing figure object has a Range1d object, and this has a start and end point. This can be printed and set by a user.
This code starts with the last line of your example.
from bokeh.plotting import show
fig = hv.render(map_tiles * taxi_trips)
fig.x_range.start = -8250000
fig.x_range.end = -8200000
# fig.x_range.reset_start = -8250000
# fig.x_range.reset_end = -8200000
# the same for the y-axis
show(fig)
Here you have to get the bokeh (underlying package) figure and set your values. This values looks a bit odd and you maybe have to play a bit with it.
Output for both options
Here is the changed output.
I hope this works for you. Good luke.

Jupyter/Plotly - how to replace or update plot from add_trace?

Consider this Jupyter Python code, which uses Plotly:
import plotly.graph_objs as go
import numpy as np
from ipywidgets import widgets
from IPython.display import display
import random
mybutton = widgets.Button(description="Redraw")
xs = np.linspace(start=0, stop=10, num=100)
fig = go.FigureWidget( layout=go.Layout() )
# NB: function needs to be written in a way, that returns np.array for input np.array!
# or - use np.vectorize, to apply it element-by-element
def TestFunc(inval):
return inval+2*random.random()
fig.add_trace(go.Scatter(x=xs, y=np.vectorize(TestFunc)(xs),
mode='lines',
name='Test'))
def on_button_clicked(b):
fig.add_trace(go.Scatter(x=xs, y=np.vectorize(TestFunc)(xs),
mode='lines',
name='Test'))
mybutton.on_click(on_button_clicked)
widgets.VBox([mybutton, fig])
What I want to do, is redraw the function anew, whenever I click the button. However, since I use add_trace in the button callback, I get new traces added - I don't get the original one replaced:
So, my question is:
How do I obtain a reference to a "trace", added with add_trace, so that I could replace it? (say, fig.traces[0] = ...)
What is the best way to redraw the figure with a new retrace, with the minimal amount of object instantiation (I guess, I could do fig = go.FigureWidget( ... ) ... upon each button click, but that would have to recreate everything; I'd think, just recreating the y array, and triggering a redraw would be more "optimized")
OK, found something - still not sure if this is the way to do it, so if someone knows better, please post...
But anyways, fig.add_trace returns a reference that you can store in a variable; eventually that variable also contains the .x and .y arrays, and the .y array can be directly replaced, like so:
import plotly.graph_objs as go
import numpy as np
from ipywidgets import widgets
from IPython.display import display
import random
mybutton = widgets.Button(description="Redraw")
xs = np.linspace(start=0, stop=10, num=100)
fig = go.FigureWidget( layout=go.Layout() )
# NB: function needs to be written in a way, that returns np.array for input np.array!
# or - use np.vectorize, to apply it element-by-element
def TestFunc(inval):
return inval+2*random.random()
mytrace = fig.add_trace(go.Scatter(x=xs, y=np.vectorize(TestFunc)(xs),
mode='lines',
name='Test'))
print(repr(mytrace))
def on_button_clicked(b):
mytrace.data[0].y = np.vectorize(TestFunc)(xs)
mybutton.on_click(on_button_clicked)
widgets.VBox([mybutton, fig])
The above code works as intended - but I'm not yet sure whether it's the most optimized way to do it...

Bokeh is behaving in mysterious way

import numpy as np
from bokeh.plotting import *
from bokeh.models import ColumnDataSource
prepare data
N = 300
x = np.linspace(0,4*np.pi, N)
y0 = np.sin(x)
y1 = np.cos(x)
output_notebook()
#create a column data source for the plots to share
source = ColumnDataSource(data = dict(x = x, y0 = y0, y1 = y1))
Tools = "pan, wheel_zoom, box_zoom, reset, save, box_select, lasso_select"
create a new plot and add a renderer
left = figure(tools = Tools, plot_width = 350, plot_height = 350, title = 'sinx')
left.circle(x, y0,source = source )
create another plot and add a renderer
right = figure(tools = Tools, plot_width = 350, plot_height = 350 , title = 'cosx')
right.circle(x, y1, source = source)
put the subplot in gridplot and show the plot
p = gridplot([[left, right]])
show(p)
something is wrong with sin graph. Don't know why 'Bokeh' is behaving like this.But if I write y's into Double or single quotation marks/inverted commas then things work fine
left.circle(x, 'y0',source = source )
right.circle(x, 'y1', source = source)
put the subplot in gridplot and show the plot
p = gridplot([[left, right]])
show(p)
Things I tried to resolve the problem
1) Restarted my notebook . (Easiest way to solve problem)
2) Generated the output into new window.
3) Generated plot separately instead of grid plot.
Please help me out to find out the reason behind the scene.
Am I doing something wrong ?
Is it a bug ?
If you want to configure multiple glyphs to share data from a single ColumnDataSource, then you always need to configure the glyph properties with the names of the columns, and not with the actual data literals, as you have done. In other words:
left.circle('x', 'y0',source = source )
right.circle('x', 'y1', source = source)
Note that I have quoted 'x' as well. This is the correct way to do things when sharing a source. When you pass a literal value (i.e., a real list or array), glyphs functions like .circle automatically synthesize a column for you as a convenience. But they use defined names based on the property, so if you share a source between two renderers, then the second call to .circle will overwrite the column 'y' column that the first call to .circle made. Which is exactly what you are seeing.
As you can imagine, this behavior is confusing. Accordingly, there is an open GitHub issue to specifically and completely disallow passing in data literals whenever the source argument is provided explicitly. I can guarantee this will happen in the near future, so if you are sharing a source, you should always and only pass in column names (i.e. strings).

how to create an object out of a seaborn plot for later reference

i'm trying to decide whether i should pursue a project involving a potentially large number of plots using matplotlib or using seaborn. the latter seems a lot more user friendly upon first examination so i am a bit biased that way. that said, i am unclear how i can create an object out of a plot that i can then call later. for example, suppose i have the following code:
x1 = np.random.randn(50)
y1 = np.random.randn(50)
data = pd.DataFrame ({})
data['x1'] = x1
data['y1'] = y1
sns.lmplot('x1', 'y1', data, fit_reg=True, ci = None)
this will display the plot as output in the iPython notebook. what i would like to do however is something like:
x = sns.lmplot('x1', 'y1', data, fit_reg=True, ci = None)
so that i can store x in a dictionary to be called later. this line runs (and will plot the output as well), but typing 'x' in a later cell displays nothing and just shows:
< seaborn.axisgrid.FacetGrid at ... >
any suggestions appreciated!
The Figure object is accessible at the FacetGrid.fig attribute.

How to add a colorbar properly in python 2.7.2?

I am affraid this sounds like a noobish question, but I am in trouble coding a colorbar around my figures.
I took some time reading the documentation and these kind of examples :
colorbar(mappable, cax=None, ax=None, use_gridspec=True, **kw)
and I can not understand what is required to make it work. Mostly I obtain this error :
AttributeError: 'AxesSubplot' object has no attribute 'autoscale_None'
Lets think about details later on. What is the easiet/more simple way to pop a colorbar along my fig which would auto scale I guess ( if it is the easiet way). Or maybe should I previously determine the min and max of my values ?
Thanks for your help !
Here is the code (Only the figure 1 matters to me) and I am aware it is poorly designed. The beginning is here to load data from previous files :
from pylab import *
import matplotlib.animation as animation
from Tkinter import Tk
from tkFileDialog import askopenfilename
Tk().withdraw() # we don't want a full GUI, so keep the root window from appearing
filename = askopenfilename() # show an "Open" dialog box and return the path to the selected file
with load(filename) as data:
XYslice = data['XYslice']
XZslice = data['XZslice']
target = data['target']
Over = data['Over']
wvl=data['wvl']
NA = data['NA']
Dt = data['t']
dz = data['dz']
Ntime,N,Nplans=shape(XZslice)
dxy=wvl/(2.0*NA)/Over
thebigone=max(XYslice[:,N/2,N/2])
XZslice[0,0,0]=thebigone
XYslice[0,0,0]=thebigone
fig=figure(1,figsize=(12,6))
ax1=fig.add_subplot(1,2,1)
xlabel(r"$x (\mu m)$")
ylabel(r"$y (\mu m)$")
ax2=fig.add_subplot(1,2,2)
xlabel(r"$x (\mu m)$")
ylabel(r"$z (\mu m)$")
I=zeros(shape(Dt))
dI=zeros(shape(Dt))
im1=ax1.imshow(XYslice[0,:,:],interpolation='none')#,extent=[-N*dxy/2.0,N*dxy/2.0,-N*dxy/2.0,N*dxy/2.0],cmap='hot')
im2=ax2.imshow(XZslice[0,:,:],interpolation='none')#,extent=[-N*dxy/2.0,N*dxy/2.0,-Nplans*dz/2.0,Nplans*dz/2.0],cmap='hot')
for ii in range(len(Dt)):
zedata=float64(((XYslice[ii,:,:]**2)[where(target==1)]).reshape(-1))
dI[ii]=(sqrt(var(zedata)))
I[ii]=(mean(zedata))
figure(2)
subplot(121)
plot(Dt,array(I),'o',Dt,array(dI))
grid('on')
subplot(122)
#plot(Dt,array(dI)/array(I))
xlabel('Dt ($\mu m^ 2$)')
grid('on')
#
def init():
im1.set_data(XYslice[0,:,:])
im2.set_data(XZslice[0,:,:])
return([im1,im2])
def animate(t):
im1.set_data(XYslice[t,:,:])
im2.set_data(XZslice[t,:,:])
return [im1,im2]
ani = animation.FuncAnimation(fig, animate, np.arange(len(Dt)),interval=250,
blit=True,init_func=init,repeat=True)
show()
Ok I find out how to do it ... It was really easy. I had to add colorbar(im2) right after the definition of im2 ...
Have a good day !

Categories