Update: I've simplified my original question quite a bit.
I want to create an animated plot but my implementation is skipping frames.
x = np.arange(0,5,0.1)
y = np.cos(3*x)
def f(i):
plt.plot(x[:i],y[:i]);
plt.gca().axis([0,5,-1,1])
plt.gca().set_title(f'{i}')
interactive(f, i=Play(value=0, min=0, max=50, step=1))
Instead of my desired step size (1), it plays at about 5 steps per frame.
The bahavior happens on both the notebook and jupyterlab, and on both the inline and notebook frontends (%matplotlib notebook)
I think this is something to do with the time taken to execute the plotting function. Try to alter the interval between plotting, looks like this is specified in milliseconds.
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import *
x = np.arange(0,5,0.1)
y = np.cos(3*x)
def f(i):
plt.plot(x[:i],y[:i]);
plt.gca().axis([0,5,-1,1])
plt.gca().set_title(f'{i}')
interactive(f, i=Play(value=0, min=0, max=50, step=1, interval=500))
Related
I am just looking for some kind of working example which will allow me to have a slider and a sine wave plot whereby I can vary the frequency in a Jupyter Notebook. All the online demos that I have Googled don't work for me. The issue seems to be that the old matplotlib graph is not erased before the new one is created.
Here is my minimal example
import ipywidgets as widgets
from IPython.display import display, clear_output
import matplotlib.pyplot as plt
import numpy as np
def f(a):
clear_output(wait=True)
fig, ax = plt.subplots()
x = np.linspace(0, 2*np.pi, 100)
y = np.sin(a*x)
ax.grid()
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.plot(x,y)
ax.set_title("y = sin(x)")
%matplotlib inline
from time import sleep
for i in range(1,10):
clear_output(wait=True)
f(i)
sleep(1)
I have also tried
out = widgets.Output(layout={'border': '1px solid black'})
with out:
widgets.interact(f, a=1)
out.clear_output()
display(out)
However nothing I try will erase the previous matplotlib graph. They just all pile up on top of each other. I admit I am floundering as I don't really understand the API that well and the example in the documentation don't work for me.
I don't expect people to fix my code as it is probably rubbish. I am just looking for a working example which will allow me to interactively control the frequency and redraw the sine wave on a Jupyter notebook.
Here is a minimal example that works for me:
from ipywidgets import interact
import ipywidgets as widgets
import matplotlib.pyplot as plt
import numpy as np
def plot(freq):
x = np.linspace(0, 2*np.pi)
y = np.sin(x * freq)
plt.plot(x, y)
interact(plot, freq = widgets.FloatSlider(value=2, min=0.1, max=5, step=0.1))
default plot:
Screenshot of notebook after moving the slider to the right:
I created the following Jupyter Notebook. Here three functions are shifted using three sliders. In the future I would like to generalise it to an arbitrary number of curves (i.e. n-curves). However, right now, the graph updating procedure is very slow and the graph itself doesn't seem to be fixed in the corrispective cell . I didn't receive any error message but I'm pretty sure that there is a mistake in the update function.
Here is the the code
from ipywidgets import interact
import ipywidgets as widgets
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import display
x = np.linspace(0, 2*np.pi, 2000)
y1=np.exp(0.3*x)*np.sin(5*x)
y2=5*np.exp(-x**2)*np.sin(20*x)
y3=np.sin(2*x)
m=[y1,y2,y3]
num_curve=3
def shift(v_X):
v_T=v_X
vector=np.transpose(m)
print(' ')
print(v_T)
print(' ')
curve=vector+v_T
return curve
controls=[]
o='vertical'
for i in range(num_curve):
title="x%i" % (i%num_curve+1)
sl=widgets.FloatSlider(description=title,min=-2.0, max=2.0, step=0.1,orientation=o)
controls.append(sl)
Dict = {}
for c in controls:
Dict[c.description] = c
uif = widgets.HBox(tuple(controls))
def update_N(**xvalor):
xvalor=[]
for i in range(num_curve):
xvalor.append(controls[i].value)
curve=shift(xvalor)
new_curve=pd.DataFrame(curve)
new_curve.plot()
plt.show()
outf = widgets.interactive_output(update_N,Dict)
display(uif, outf)
Your function is running on every single value the slider moves through, which is probably giving you the long times to run you are seeing. You can change this by adding continuous_update=False into your FloatSlider call (line 32).
sl=widgets.FloatSlider(description=title,
min=-2.0,
max=2.0,
step=0.1,
orientation=o,
continuous_update=False)
This got me much better performance, and the chart doesn't flicker as much as there are vastly fewer redraws. Does this help?
When plotting using matplotlib, I ran into an interesting issue where the y axis is scaled by a very inconvenient quantity. Here's a MWE that demonstrates the problem:
import numpy as np
import matplotlib.pyplot as plt
l = np.linspace(0.5,2,2**10)
a = (0.696*l**2)/(l**2 - 9896.2e-9**2)
plt.plot(l,a)
plt.show()
When I run this, I get a figure that looks like this picture
The y-axis clearly is scaled by a silly quantity even though the y data are all between 1 and 2.
This is similar to the question:
Axis numerical offset in matplotlib
I'm not satisfied with the answer to this question in that it makes no sense to my why I need to go the the convoluted process of changing axis settings when the data are between 1 and 2 (EDIT: between 0 and 1). Why does this happen? Why does matplotlib use such a bizarre scaling?
The data in the plot are all between 0.696000000017 and 0.696000000273. For such cases it makes sense to use some kind of offset.
If you don't want that, you can use you own formatter:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker
l = np.linspace(0.5,2,2**10)
a = (0.696*l**2)/(l**2 - 9896.2e-9**2)
plt.plot(l,a)
fmt = matplotlib.ticker.StrMethodFormatter("{x:.12f}")
plt.gca().yaxis.set_major_formatter(fmt)
plt.show()
I am changing the font-sizes in my python pandas dataframe plot. The only part that I could not change is the scaling of y-axis values (see the figure below).
Could you please help me with that?
Added:
Here is the simplest code to reproduce my problem:
import pandas as pd
start = 10**12
finish = 1.1*10**12
y = np.linspace(start , finish)
pd.DataFrame(y).plot()
plt.tick_params(axis='x', labelsize=17)
plt.tick_params(axis='y', labelsize=17)
You will see that this result in the graph similar to above. No change in the scaling of the y-axis.
Ma
There are just so many features that you can control with the plotting capabilities of pandas, which leverages matplotlib. I found that seaborn is a lot easier to produce pretty charts, and you have a lot more control over the parameters of your plots.
This is not the most elegant solution, but it works; however, it has a seborn dependency:
%pylab inline
import pandas as pd
import seaborn as sns
import numpy as np
sns.set(style="darkgrid")
sns.set(font_scale=1.5)
start = 10**12
finish = 1.1*10**12
y = np.linspace(start , finish)
pd.DataFrame(y).plot()
plt.tick_params(axis='x', labelsize=17)
plt.tick_params(axis='y', labelsize=17)
I use Jupyter Notebook an that's why I use %pylab inline. The key element here is the use of
font_scale=1.5
Which you can set to whatver you want that produces your desired result. This is what I get:
Using 1.5.1 in Python 2.7.
I'm creating a figure, adding an axes object to it, creating a canvas, and putting it into a window. To draw a simple graph, I set the X and Y limits in the axes object, and then call the plot member function with a numpy arange of values and an array of y values of the same length, along with a few formatting options.
What I get is a nice graph of my data, but it is drawn as a closed curve, meaning that there is a diagonal line leading from the end of my graph back to the beginning.
Why would it do this? I can see the occasional utility of an option that does this, when the X values aren't monotonically increasing (say, to draw a polygon), but it hardly seems like a reasonable default. I don't see any axes attribute that would affect this, or any plot parameter. Does anyone know how to make it not wrap around like this?
EDIT: here is some sample code. It assumes PyGTK as the GUI environment:
import numpy
import gtk
import matplotlib
from matplotlib.figure import Figure
from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas
class junk:
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.connect('destroy', self.destroy)
self.window.set_title('junk')
self.window.resize(400, 400)
self.figure = Figure()
self.axes = self.figure.add_axes((0, 0, 1, 1))
self.canvas = FigureCanvas(self.figure)
self.canvas.show()
self.window.add(self.canvas)
self.axes.set_xlim(-10, 12)
self.axes.set_ylim(-1, 122)
x = numpy.arange(-9, 12)
self.axes.plot(x, x * x, linestyle = 'solid')
self.canvas.draw()
self.window.show_all()
def destroy(self, widget, data = None):
gtk.main_quit()
def main(self):
gtk.main()
if __name__ == '__main__':
app = junk()
app.main()
This displays an off-center parabola, and the result looks like this:
Now change the lower Y limit from -1 to 1, so that it clips the bottom a little, and the result looks like this:
This shows that if more than one path is needed to draw the graph, each one has the spurious wraparound.
I'm doing this on Windows, but I had this same problem a couple years ago running on a Gumstix SOM running Linux.
I can not reproduce your issue with the given code
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x = np.arange(-9, 12)
ax.plot(x, x*x)
plt.show()
A line is drawn between the points in the order you pass them in. This is the behavior so you can plot things with are not strict functions of x.
BrenBarn identified the solution in the comment to the original post: use the GTKAgg backend instead of the GTK backend. Thanks.