Martplotlib slider does not work - python

I need to make some plots with multiple parameters, and I choosed to make it more interactive with matplotlib sliders. For some practise before my actual task I tried to make it relativelly simple, but my sliders does not work. Here is the code, which is inspired from here.
Code:
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.widgets as mw
from scipy import stats
mu = 1
sigma = 3
a = 2
b = 3
axis_color = 'lightgoldenrodyellow'
x = [i for i in range(-100,100,1)]
normal_pdf = stats.norm.pdf(x, mu, sigma)
a_normal_pdf = [i*a for i in normal_pdf]
ab_normal_pdf = [i*b*a for i in normal_pdf]
fig = plt.figure()
ax1 = fig.add_subplot(221)
ax2 = fig.add_subplot(222)
ax3 = fig.add_subplot(223)
ax4 = fig.add_subplot(224)
ax4.axis('off')
#sliders
a_slider_ax = fig.add_axes([0.6, 0.25, 0.25, 0.03], axisbg=axis_color)
a_slider = mw.Slider(a_slider_ax, 'a', 1, 100, valinit = a)
b_slider_ax = fig.add_axes([0.6, 0.4, 0.25, .03], axisbg = axis_color)
b_slider = mw.Slider(b_slider_ax, 'b', 1, 100, valinit = b)
#function for sliders
def sliders_on_change(val):
a_normal_pdf.set_ydata([x*a_slider for x in normal_pdf])
ab_normal_pdf.set_ydata([x*a_slider*b_slider for x in normal_pdf])
fig.canvas.draw_idle()
a_slider.on_changed(sliders_on_change)
b_slider.on_changed(sliders_on_change)
ax1.plot(x, normal_pdf, 'r-')
ax2.plot(x, a_normal_pdf, 'bo')
ax3.plot(x, ab_normal_pdf, 'g*')
plt.show()
I do not fully understand HOW sliders should work, so its maybe the problem instead of idle issue as here, because I tried it in spyder and in jupyter as well, no difference. I can move with sliders, but I cant change the a_normal_pdf nor ab_normal_pdf.

You have two issues in your code:
using the slider object a_slider in place of the slider's current value a_slider.val
the method set_ydata changes the y-data of a Line2D plot object (I saved it in a variable p1 to be able to modify it)
Modified code (hope this helps)
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.widgets as mw
from scipy import stats
mu = 1
sigma = 3
a = 2
b = 3
axis_color = 'lightgoldenrodyellow'
x = [i for i in range(-100,100,1)]
normal_pdf = stats.norm.pdf(x, mu, sigma)
a_normal_pdf = [i*a for i in normal_pdf]
ab_normal_pdf = [i*b*a for i in normal_pdf]
fig = plt.figure()
ax1 = fig.add_subplot(221)
ax2 = fig.add_subplot(222)
ax3 = fig.add_subplot(223)
ax4 = fig.add_subplot(224)
ax4.axis('off')
#sliders
a_slider_ax = fig.add_axes([0.6, 0.25, 0.25, 0.03], axisbg=axis_color)
a_slider = mw.Slider(a_slider_ax, 'a', 1, 100, valinit = a)
b_slider_ax = fig.add_axes([0.6, 0.4, 0.25, .03], axisbg = axis_color)
b_slider = mw.Slider(b_slider_ax, 'b', 1, 100, valinit = b)
#function for sliders
def sliders_on_change(val):
p1.set_ydata([x*a_slider.val for x in normal_pdf])
p2.set_ydata([x*a_slider.val*b_slider.val for x in normal_pdf])
fig.canvas.draw_idle()
a_slider.on_changed(sliders_on_change)
b_slider.on_changed(sliders_on_change)
p1,=ax1.plot(x, normal_pdf, 'r-')
p2,=ax2.plot(x, a_normal_pdf, 'bo')
p3,=ax3.plot(x, ab_normal_pdf, 'g*')
plt.show()

Related

Matplot lib animation not working as expected

I have 4 variables like this:
# generate 4 random variables from the random, gamma, exponential, and uniform distributions
x1 = np.random.normal(-2.5, 1, 10000)
x2 = np.random.gamma(2, 1.5, 10000)
x3 = np.random.exponential(2, 10000)+7
x4 = np.random.uniform(14,20, 10000)
And I need to create one figure with 4 subplots.
so I tried this:
plt.figure(figsize=(9,3))
plt.subplot(1,4,1)
plt.hist(x1, normed=True, bins=20, alpha=0.5)
plt.subplot(1,4,2)
plt.hist(x2, normed=True, bins=20, alpha=0.5)
plt.subplot(1,4,3)
plt.hist(x3, normed=True, bins=20, alpha=0.5)
plt.subplot(1,4,4)
plt.hist(x4, normed=True, bins=20, alpha=0.5)
plt.axis([-7,21,0,0.6])
And I got this result
Now I want to create an animation on the subplots, so I did the following (trying one subplot only)
import matplotlib.animation as animation
def update(curr):
if curr == n:
a.event_source.stop()
plt.cla()
plt.figure(figsize=(9,3))
plt.subplot(1,4,1)
plt.hist(x1, normed=True, bins=20, alpha=0.5)
plt.axis([-7,21,0,0.6])
plt.gca().set_title('Sample')
plt.gca().set_ylabel('Frequency')
plt.gca().set_xlabel('Value')
plt.annotate('n = {}'.format(curr), [3.27])
fig = plt.figure()
a = animation.FuncAnimation(fig, update, interval=100)
However the end result is empty, nothing is shown.
Any idea?
I re-structured your code in order to plot the animation of the 4 subplots.
Without any specific indication on what you want to see changing between one frame and the next, I assume the number of sample drawn from each distribution is inscreasing in each frame by 10.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
def update(curr):
N = 10*curr
x1 = np.random.normal(-2.5, 1, N)
x2 = np.random.gamma(2, 1.5, N)
x3 = np.random.exponential(2, N) + 7
x4 = np.random.uniform(14, 20, N)
ax[0].cla()
ax[0].hist(x1, bins = 20, alpha = 0.5, color = 'blue', edgecolor = 'blue')
ax[0].set_title('Normal')
ax[0].set_ylabel('Frequency')
ax[0].set_xlabel('Value')
ax[0].set_xlim(-6, 1)
ax[1].cla()
ax[1].hist(x2, bins = 20, alpha = 0.5, color = 'blue', edgecolor = 'blue')
ax[1].set_title('Gamma')
ax[1].set_ylabel('Frequency')
ax[1].set_xlabel('Value')
ax[1].set_xlim(0, 12)
ax[2].cla()
ax[2].hist(x3, bins = 20, alpha = 0.5, color = 'blue', edgecolor = 'blue')
ax[2].set_title('Exponential')
ax[2].set_ylabel('Frequency')
ax[2].set_xlabel('Value')
ax[2].set_xlim(7, 25)
ax[3].cla()
ax[3].hist(x4, bins = 20, alpha = 0.5, color = 'blue', edgecolor = 'blue')
ax[3].set_title('Uniform')
ax[3].set_ylabel('Frequency')
ax[3].set_xlabel('Value')
ax[3].set_xlim(14, 20)
ax[0].set_ylim(0, 250)
fig.suptitle(f'Number of samples: {N}')
plt.tight_layout()
fig, ax = plt.subplots(1, 4, figsize = (9, 3), sharey = 'all')
a = FuncAnimation(fig, update, interval = 100, frames = 81)
plt.show()

Modifying saved plot with matplotlib

I am having a problem right now. I have run an extremely heavy simulation and, thus, generated a plot with matplotlib containing the results and saved it (as .jpg). However, there are some elemnts of the plot I would like to change, such as labels size and one vertical line. Is there a straighforward way to do this using matplotlib? I know I could have stored the data and now just replot changing the parameters (and, actually, I have done this), but I was wondering whether there is an easier way. Maybe something like:
fig, ax = plt.figure(path_to_figure)
ax.set_ylabel("Y_label")
...
You can refer to below example, which gives you more idea on how you can do this while plotting everything.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
%matplotlib inline
plt.rc('text', usetex=True)
def f(t):
return t ** 2
t1 = np.arange(0.0, 2.0, 0.1)
noise = np.random.randn(len(t1)) * 0.04
# x coordinates for the lines
xcoords = [0.1, 0.3, 0.5]
# colors for the lines
colors = ['r','k','b']
fig = plt.figure(figsize=(4, 3), dpi=200)
ax = fig.add_subplot(1, 1, 1)
plt.scatter(t1, f(t1 + noise), color = 'hotpink', label='Values obtained by experiment', edgecolors='k')
plt.plot(t1, f(t1), ls='solid', label='Theoretical expectation', color='b')
plt.title(r'This is latex title example $\mathbf{E = m \times c^2}$', fontsize='small')
for xc,c in zip(xcoords,colors):
plt.axvline(x=xc, label='line at x = {}'.format(xc), c=c)
plt.grid()
plt.legend(loc=0)
If you want to make all the fonts bold, you can also use below code to make everything bold:
font = {'weight' : 'bold',
'size' : 14 }
plt.rc('font', **font)
def f(t):
return t ** 2
t1 = np.arange(0.0, 2.0, 0.1)
noise = np.random.randn(len(t1)) * 0.04
# x coordinates for the lines
xcoords = [0.1, 0.3, 0.5]
# colors for the lines
colors = ['r','k','b']
fig = plt.figure(figsize=(4, 3), dpi=200)
ax = fig.add_subplot(1, 1, 1)
plt.scatter(t1, f(t1 + noise), color = 'hotpink', label='Values obtained by experiment', edgecolors='k')
plt.plot(t1, f(t1), ls='solid', label='Theoretical expectation', color='b')
plt.title(r'This is latex title example $\mathbf{E = m \times c^2}$', fontsize='small')
plt.xlabel("This is X-label.", fontsize=12)
plt.ylabel("This is Y-label.", fontsize=16)
for xc,c in zip(xcoords,colors):
plt.axvline(x=xc, label='line at x = {}'.format(xc), c=c)
plt.grid()
plt.legend(loc=(1.15,0.2))

Struggling to get widgets working in python

I don't know why but I am really struggling to get widgets working well in python. I try to look at examples about how to use them but I don't know how to extrapolate that to get it to work with my code. I am trying to get a figure to display widgets such that the type, frequency, phase, and other variables adjust the graph itself.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.widgets as widgets
from scipy import signal
from matplotlib.widgets import RadioButtons
A = 1
ang_f = 5
t = np.linspace(0, 4*np.pi, 1000)
phase = 0
s0 = A*np.sin(ang_f*t + phase)
s2 = A*signal.sawtooth(ang_f*t + phase)
s1 = A*signal.square(ang_f*t + phase)
fig, ax = plt.subplots()
l, = ax.plot(t, s1, lw=2, color='red')
plt.subplots_adjust(left=0.4)
def sinf(x, omega):
return np.sin(omega*x)
def sliderCallback(val):
# """ 'val' is the current value selected by the slider
# Recalculate sine values with val as the frequency """
axesHandle.set_ydata(sinf(x, val))
plt.draw() # Redraw the axes
def clickcallback(val):
# 'val' is the current value selected by the slider
# Recalculate sine values with val as the frequency
axesHandle.set_ydata(sinf(x, val))
plt.draw() # Redraw the axes
def closeCallback(event):
plt.close('all') # Close all open figure windows
fig = plt.figure(figsize=(7, 5))
ax = plt.axes([0.1, 0.2, 0.6, 0.7])
axesHandle, = plt.plot(x, sinf(x, 1), lw=2, color='red')
# Add axis to contain the slider
fax = plt.axes([0.1, 0.04, 0.35, 0.03]) # Frequency
tax = plt.axes([0.1, 0.12, 0.35, 0.03]) # Time
sax_3 = plt.axes([0.60, 0.1, 0.35, 0.03]) # Number of points
pax = plt.axes([0.60, 0.05, 0.35, 0.03]) # Phase
rax = plt.axes([0.85, 0.65, 0.12, 0.15]) # Type
bax = plt.axes([0.85, 0.85, 0.1, 0.1]) # Close
pointshandle = widgets.Slider(sax_3, 'Number of points', 1, 200,
valfmt='%0.0f')
pointshandle.on_changed(sliderCallback)
graphchoice = widgets.RadioButtons(rax, ('Sine', 'Squarewave', 'Sawtooth'))
graphchoice.on_clicked(clickcallback)
freqhandle = widgets.Slider(fax, 'Frequancy (Hz)', 0, 5, valinit=1)
freqhandle.on_changed(sliderCallback)
phasehandle = widgets.Slider(pax, 'Phase', 0, 0*np.pi, valinit=0)
phasehandle.on_changed(sliderCallback)
timehandle = widgets.Slider(tax, 'Time (s)', 1, 10, valinit=1)
timehandle.on_changed(sliderCallback)
buttonHandle = widgets.Button(bax, 'Close')
buttonHandle.on_clicked(closeCallback)
def hzfunc(label):
hzdict = {'Sine': s0, 'Squarewave': s1, 'Sawtooth': s2}
ydata = hzdict[label]
l.set_ydata(ydata)
plt.draw()
graphchoice.on_clicked(hzfunc)
I'm really lost so any tips to put me on the right path would be much appreciated, im just so confused atm.

Use slider for two different lines in one subplot matplotlib

I am plotting six subplots in one figure in matplotlib. I am plotting one function with different parameter values using matplotlib sliders. There are two lines in one subplot, each representing function. I want to see where they cross and how they behave if I am changing function parameters. But I havent figured out how to plot two lines and change ydata for two lines in one subplot with sliders.
Here is part of code:
#imports
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.widgets as mw
import math
import numpy as np
#set variables
E0 = 0.5
E1 = .0003
V = .3
#x axis
N = [i for i in range(10000)]
#functions
V_fc_list = [V for n in N]
E_list = [E0*math.exp(-E1*n) for n in N]
#subplots
fig = plt.figure()
ax1 = fig.add_subplot(321)
ax2 = fig.add_subplot(322)
ax3 = fig.add_subplot(323)
ax4 = fig.add_subplot(324)
ax5 = fig.add_subplot(325)
ax6 = fig.add_subplot(326)
#sliders place
ax6.axis('off')
#Sliders
axis_color = 'lightgoldenrodyellow'
E0_slider_ax = fig.add_axes([0.57, 0.3, 0.3, 0.02], axisbg=axis_color)
E1_slider_ax = fig.add_axes([0.57, 0.25, 0.3, .02], axisbg = axis_color)
V_slider_ax = fig.add_axes([0.57, 0.2, 0.3, .02], axisbg = axis_color)
E0_slider = mw.Slider(E0_slider_ax, r'$\epsilon_0$', valmin = 0, valmax = 1, valinit = E0)
E0_slider.label.set_size(15)
E1_slider = mw.Slider(E1_slider_ax, r'$\epsilon_1$', 0.0001, 0.003, valinit = E1)
E1_slider.label.set_size(15)
V_slider = mw.Slider(V_slider_ax, r'$V_c$', 0.001, 0.99, valinit = V)
V_slider.label.set_size(15)
#slider function HERE IS THE MISTAKE
def sliders_on_change(val):
p2.set_ydata([V_slider.val for n in N])
p2.set_ydata([E0_slider.val*math.exp(-E1_slider.val*n) for n in N])
fig.canvas.draw_idle()
V_slider.on_changed(sliders_on_change)
E0_slider.on_changed(sliders_on_change)
E1_slider.on_changed(sliders_on_change)
Here is the last part of error from python
File "C:/Users/Robert/Desktop/python/multidif_S.py", line 109, in <module>
p2,= ax2.plot(N, V_fc_list, 'r-', N, E_list, 'b-', lw = 3)
ValueError: too many values to unpack (expected 1)
Thx for any help!
I changed only a few things in your code to get this:
#imports
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.widgets as mw
import math
import numpy as np
#set variables
E0 = 0.5
E1 = .0003
V = .3
#x axis
N = [i for i in range(10000)]
#functions
V_fc_list = [V for n in N]
E_list = [E0*math.exp(-E1*n) for n in N]
#subplots
fig = plt.figure()
ax1 = fig.add_subplot(321)
ax2 = fig.add_subplot(322)
ax3 = fig.add_subplot(323)
ax4 = fig.add_subplot(324)
ax5 = fig.add_subplot(325)
ax6 = fig.add_subplot(326)
#sliders place
ax6.axis('off')
#Sliders
axis_color = 'lightgoldenrodyellow'
E0_slider_ax = fig.add_axes([0.57, 0.3, 0.3, 0.02], axisbg=axis_color)
E1_slider_ax = fig.add_axes([0.57, 0.25, 0.3, .02], axisbg = axis_color)
V_slider_ax = fig.add_axes([0.57, 0.2, 0.3, .02], axisbg = axis_color)
E0_slider = mw.Slider(E0_slider_ax, r'$\epsilon_0$', valmin = 0, valmax = 1, valinit = E0)
E0_slider.label.set_size(15)
E1_slider = mw.Slider(E1_slider_ax, r'$\epsilon_1$', 0.0001, 0.003, valinit = E1)
E1_slider.label.set_size(15)
V_slider = mw.Slider(V_slider_ax, r'$V_c$', 0.001, 0.99, valinit = V)
V_slider.label.set_size(15)
# Here I introduce the plots p1 and p2. Your code didn't have any plots.
p1 = ax1.plot(np.zeros_like(N)) # plot1
p2 = ax1.plot(np.zeros_like(N)) # plot2, both in ax1
#slider function HERE IS THE MISTAKE
def sliders_on_change(val):
p1[0].set_ydata([V_slider.val for n in N]) # update p1
p2[0].set_ydata([E0_slider.val*math.exp(-E1_slider.val*n) for n in N]) # update p2
ax1.relim() # rescale the shown area (like an automatic call of ax1.set_xlim and ax1.set_ylim with proper inputs)
ax1.autoscale_view() # taken from this question: http://stackoverflow.com/questions/10984085/automatically-rescale-ylim-and-xlim-in-matplotlib
fig.canvas.draw()
V_slider.on_changed(sliders_on_change)
E0_slider.on_changed(sliders_on_change)
E1_slider.on_changed(sliders_on_change)
You see I commented where I changed something. Mostly this was adding the plots p1 and p2 and rescaling on update.
I hope this works for you. I can't help you with that error code because the code you provided does not even have 109 lines...
If you have two lines in a plot, you need to unpack them to two different variables,
p2,p3 = ax2.plot(N, V_fc_list, 'r-', N, E_list, 'b-', lw = 3)
You can then set the data on the two lines as follows:
def sliders_on_change(val):
p3.set_ydata([V_slider.val for n in N])
p2.set_ydata([E0_slider.val*math.exp(-E1_slider.val*n) for n in N])
fig.canvas.draw_idle()

How to make two sliders in matplotlib

I would like to make two sliders in matplotlib to manually change N and P values in my predator-prey model:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
def lotka(x,t,params):
N, P = x
alpha, beta, gamma, delta = params
derivs = [alpha*N - beta*N*P, gamma*N*P - delta*P]
return derivs
N=2
P=1
alpha=3
beta=0.5
gamma=0.4
delta=3
params = [alpha, beta, gamma, delta]
x0=[N,P]
maxt = 20
tstep = 0.01
t=np.arange(0,maxt,tstep)
equation=odeint(lotka, x0, t, args=(params,))
plt.plot(t,equation)
plt.xlabel("Time")
plt.ylabel("Population size")
plt.legend(["Prey", "Predator"], loc="upper right")
plt.title('Prey & Predator Static Model')
plt.grid(color="b", alpha=0.5, linestyle="dashed", linewidth=0.5)
This is my code which produces a graph for fixed initial values of N and P. However, I'd like to change them to see how the plot changes. And for this, I'd like to use sliders like: http://matplotlib.org/users/screenshots.html#slider-demo but I do not know how to add this into my code...
Could anyone please give me any direction? Many thanks!! xx
From the example, hope the comments help you understand what's what:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons
from scipy.integrate import odeint
# Function to draw
def lotka(x, t, params):
N, P = x
alpha, beta, gamma, delta = params
derivs = [alpha*N - beta*N*P, gamma*N*P - delta*P]
return derivs
# Parameters
Nmin = 1
Nmax = 100
Pmin = 1
Pmax = 100
N0 = 2
P0 = 1
alpha = 3
beta = 0.5
gamma = 0.4
delta = 3
params = [alpha, beta, gamma, delta]
x0=[N0,P0]
maxt = 20
tstep = 0.01
# Initial function values
t = np.arange(0, maxt, tstep)
prey, predator = odeint(lotka, x0, t, args=(params,)).T
# odeint returne a shape (2000, 2) array, with the value for
# each population in [[n_preys, n_predators], ...]
# The .T at the end transponses the array, so now we get each population
# over time in each line of the resultint (2, 2000) array.
# Create a figure and an axis to plot in:
fig = plt.figure()
ax = fig.add_axes([0.10, 0.3, 0.8, 0.6])
prey_plot = ax.plot(t, prey, label="Prey")[0]
predator_plot = ax.plot(t, predator, label="Predator")[0]
ax.set_xlabel("Time")
ax.set_ylabel("Population size")
ax.legend(loc="upper right")
ax.set_title('Prey & Predator Static Model')
ax.grid(color="b", alpha=0.5, linestyle="dashed", linewidth=0.5)
ax.set_ylim([0, np.max([prey, predator])])
# create a space in the figure to place the two sliders:
axcolor = 'lightgoldenrodyellow'
axis_N = fig.add_axes([0.10, 0.1, 0.8, 0.03], facecolor=axcolor)
axis_P = fig.add_axes([0.10, 0.15, 0.8, 0.03], facecolor=axcolor)
# the first argument is the rectangle, with values in percentage of the figure
# size: [left, bottom, width, height]
# create each slider on its corresponding place:
slider_N = Slider(axis_N, 'N', Nmin, Nmax, valinit=N0)
slider_P = Slider(axis_P, 'P', Pmin, Pmax, valinit=P0)
def update(val):
# retrieve the values from the sliders
x = [slider_N.val, slider_P.val]
# recalculate the function values
prey, predator = odeint(lotka, x, t, args=(params,)).T
# update the value on the graph
prey_plot.set_ydata(prey)
predator_plot.set_ydata(predator)
# redraw the graph
fig.canvas.draw_idle()
ax.set_ylim([0, np.max([prey, predator])])
# set both sliders to call update when their value is changed:
slider_N.on_changed(update)
slider_P.on_changed(update)
# create the reset button axis (where its drawn)
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
# and the button itself
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')
def reset(event):
slider_N.reset()
slider_P.reset()
button.on_clicked(reset)
Notice, however, you should have shown how you tried to adapt the example to what you had and how it was misbehaving.
Nevertheless, welcome to Stackoverflow.
So, I have tried with this code:
from scipy import integrate
from matplotlib.widgets import Slider, Button, RadioButtons
fig, ax = plt.subplots()
plt.subplots_adjust(left=0.25, bottom=0.25)
plt.xlabel("Time")
plt.ylabel("Population size")
plt.legend(["Prey", "Predator"], loc="upper right")
plt.title('Prey & Predator Static Model')
plt.grid(color="b", alpha=0.5, linestyle="dashed", linewidth=0.5)
l1, l2 = plt.plot(t, equation)
axcolor = 'b'
ax_N = plt.axes([0.25, 0.1, 0.65, 0.03], axisbg=axcolor)
ax_P = plt.axes([0.25, 0.15, 0.65, 0.03], axisbg=axcolor)
sN = Slider(ax_N, 'N', 0, 80, valinit=1)
sP = Slider(ax_P, 'P', 0, 80, valinit=1)
def update(val):
N = N*sN.val
P = P*sP.val
x = equation
fig.canvas.draw_idle()
l1, l2.set_ydata(y)
ax.set_ylim(y.min(), y.max())
draw()
sN.on_changed(update)
sP.on_changed(update)
plt.show()
I could not manipulate the sliders. Thank you so much #berna1111

Categories