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...
Related
I am making a animated bar plot for basic bubble sort . It runs pretty good. But doesn't repeat itself (loop). I am trying it in jupyter notebook , I added %matplotlib qt,
Why won't my animFunc repeat although I have set the repeat to True .
x=["1","2","3","4","5","6","7","8","9","10"]
y=[7,8,5,3,1,9,4,2,10,6]
temp=0
def animator_X():
for a in range(len(y)-1):
for b in range(len(y)-a-1):
if y[b]>y[b+1]:
temp = y[b]
y[b]=y[b+1]
y[b+1]=temp
yield y
fig,ax = plt.subplots(figsize=(7,5))
def init():
ax.clear()
y=[7,8,5,3,1,9,4,2,10,6]
plt.bar(x,y,color=['blue'])
def animX(i):
ax.clear()
plt.bar(x,y,color=['blue'])
return plt
animx = FuncAnimation(fig,animX,frames=animator_X,interval=1000,init_func=init,repeat=True)
plt.show()
You aren't resetting the main y variable when it repeats the init function after a run.
Try:
%matplotlib notebook
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x=["1","2","3","4","5","6","7","8","9","10"]
y=[7,8,5,3,1,9,4,2,10,6]
temp=0
def animator_X():
for a in range(len(y)-1):
for b in range(len(y)-a-1):
if y[b]>y[b+1]:
temp = y[b]
y[b]=y[b+1]
y[b+1]=temp
print(y)
yield y
fig,ax = plt.subplots(figsize=(7,5))
def init():
global y
ax.clear()
y=[7,8,5,3,1,9,4,2,10,6]
plt.bar(x,y,color=['blue'])
def animX(i):
ax.clear()
plt.bar(x,y,color=['blue'])
return plt
anim = animation.FuncAnimation(fig,animX,frames=animator_X,interval=100,init_func=init)
plt.show()
That code will run in sessions launched from here. Go there and press launch binder. When it comes up, you can paste in the code.
I suspect in OP's code the addition of the global y line in the init() function will fix the OP's version.
Further explanation
It does keep repeating with the code posted in the OP because the kernel keeps running on that cell after the first pass.
A y object that is local solely to the init function is getting reset within the scope of the init() function when it repeats after the first pass. I don't know enough about how FuncAnimation() decides to update/and what it displays then to tell you why OP code without updating y in the main scope results in it showing the init() state and doesn't instead flash to the init state and then back to the sorted state. The kernel is still running and so maybe it is flashing between those two yet the init() dominates for some reason? That's speculation because the FuncAnimation() is so specialized that it doesn't display what is put in print statements inside the init or main function that gets animated, and so probing what's going on separate from the plot, in a simplistic manner, is not easy.
Im using bokeh server to plot a line graph, where theres a checkbox button that will flip the line if its checked. If its unchecked I want to see the original version of the line (unflipped). Following the flip/unflip, a second function is called to perform some other calculations:
import numpy as np
from bokeh.io import curdoc
from bokeh.layouts import row, widgetbox, layout
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import CheckboxGroup
from bokeh.plotting import figure
def flip_signal(signal, flip):
if flip:
signal = -signal
else:
signal = signal
return signal
N = 200
x = np.linspace(0, 4*np.pi, N)
y = np.sin(x)
source = ColumnDataSource(data=dict(x=x, y=y))
plot = figure(plot_height=500, plot_width=850, title="test",
tools="crosshair,pan,reset,save,wheel_zoom")
line_orig = plot.line('x', 'y', source=source, line_width=1, line_alpha=1)
flip_signal_btn = CheckboxGroup(labels=["Flip signal"])
def update_flip(attrname, old, new):
if 0 in flip_signal_btn.active:
flip = True
else:
flip = False
# Update plot
source.data = dict(x=source.data['x'], y=flip_signal(source.data['y'], flip))
def update_peaks(attrname, old, new):
# do something else
pass
for w in [flip_signal_btn]:
w.on_change('active', update_flip)
w.on_change('active', update_peaks)
options = widgetbox(flip_signal_btn)
doc_layout = layout(row([options], height=200))
curdoc().add_root(row(plot, doc_layout, width=800))
curdoc().title = "checkbox"
The checkbox only seems to call update_flip when its checked, so in order to flip (or unflip) the signal I need to click it twice. For example, when I uncheck the box nothing happens, but I'm expecting it to unflip the signal. Rather it only unflips the signal if I uncheck and then check the box again
The callback is being invoked with the correct values on every button click, as can be verified with some print statements. The error is in your logic. Since you are operating on the current signal, rather than some original signal, you presumably always want to flip every time, unconditionally. Currently, you are only flipping every other button push, because this flip_signal(..., False) just returns the signal passed in, as-is. Changing the update_flip callback to always flip yields the behavior you want:
def update_flip(attrname, old, new):
# flip the *current* data on *every* button toggle
source.data = dict(x=source.data['x'], y=flip_signal(source.data['y'], True))
For your logic to work you would need a different function than your current flip_signal. You would need a function that always returns the original unflipped signal on False, and always returns the flipped signal on True. Contrast this with the current flip_signal, if you call it with False it gives you back whatever you passed in, regardless of whether it is the flipped or unflipped signal.
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.
I am working on a visualization of different vector fields.
For this purpose I am using the Mayavi Library in Python 2.7 (I think), to create a Image Plane Widget (IPW) and a slider to change the data of the vector field while the visualization is open, but my IPW won't change.
It works if I render the IPW new each time the slider is changed, but that's not what I want.
Is there a way to change the data of an IPW while the program is running without rendering a new Plane each time?
I have written following code:
import numpy as np
from mayavi import mlab
from matplotlib.scale import scale_factory
from traits.api import HasTraits, Range, Instance, Array, \
on_trait_change
from traitsui.api import View, Item, Group
from mayavi.core.pipeline_base import PipelineBase
from mayavi.core.ui.api import MayaviScene, SceneEditor, \
MlabSceneModel
class Modell(HasTraits):
p = Array
n = Range(0, 9, 5)
#p is a 4 dimensional Array p[10][20][20][20]
scene = Instance(MlabSceneModel, ())
plot = Instance(PipelineBase)
#on_trait_change('n,scene.activated')
def update_plot(self):
self.src = mlab.pipeline.scalar_field(self.p[self.n])
if self.plot is None:
self.plot = self.scene.mlab.pipeline.image_plane_widget(self.src,
plane_orientation='z_axes',
slice_index=10,
vmin=0, vmax=120)
else:
'''here should be the update function, i tried using mlab_source.set(self.src=self.src)
and all variations of expressions in the brackets but nothing worked.
I also searched for functions in IPW itself but didn't find something that could help me.'''
#The layout of the dialog created
view = View(Item('scene', editor=SceneEditor(scene_class=MayaviScene),
height=400, width=400, show_label=False),
Group('_', 'n'),
resizable=True,
)
my_model = Modell(p=p)
my_model.configure_traits()
I tried updating the pipeline and updating the data with self.plot.update_pipeline() and self.plot.update_data() but this doesn't work either.
Ok I found the solution for my problem, the trick is to change the data directly through the pipeline. So in my code I just have to set the following command into the else segment:
self.plot.parent.parent.scalar_data = self.p[self.n]
I have a dictionary with data. For every entry I would like to display plots for 1 second and move to the next one. The plots to display are already coded in external scripts. I would like to do this automatically. So I loop through the dict, display first set of plots[0], close the plots[0], display plots[1] close plots[1] ... I would like to set up display time for let say 1 second and have the plot as full screen. The problem that during the presentation I don't want to touch the computer.
import pylab as pl
import numpy as np
x = np.arange(-np.pi, np.pi, 0.1) # only for the example purpose
myDict = {"sin":np.sin(x), "cos":np.cos(x), "exp":np.exp(x)}
for key in myDict:
print myDict[key]
pl.plt.plot(myDict[key]) # in origin coming from external function
pl.plt.plot(x) # in origin coming from external function
pl.plt.show()
Does anyone know what function should be used and how to modify above?
A simple method is to use plt.pause(1). A more sophisticated method is to usethe matplotlib.animate module. See pylab.ion() in python 2, matplotlib 1.1.1 and updating of the plot while the program runs
example, api, tutorial
import time
import pylab as pl
import numpy as np
pl.ion()
x = np.arange(-np.pi, np.pi, 0.1) # only for the example purpose
myDict = {"sin":np.sin, "cos":np.cos, "exp":np.exp}
for key in myDict:
print myDict[key]
pl.clf()
y = myDict[key](x)
pl.plt.plot(x, y, label=key)
pl.plt.draw()
time.sleep(1)