Related
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.
I have the following equation:
y = ((b-6(x**k))/c)**(1/k)
k = 10/(6+c)
I know that when k > 1 then y is concave and when 0 < k < 1 then y is convex. However, the problem is that in the generated plot it does not matter whatever the value of k is, it always generates a concave y. I was wondering if anybody can help me to figure out what is the problem.
Codes to generate the dynamic plot:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons
fig, ax = plt.subplots()
plt.subplots_adjust(left=0.25, bottom=0.25)
x = np.arange(0.0, 1.0, 0.001)
b_init = 1
c_init = 0
k = 10/(6+c_init)
delta_f = 1.0
y = ((b_init-6*(x**k))/c_init)**(1/k)
l, = plt.plot(x, y, lw=2)
ax.margins(x=0)
axcolor = 'lightgoldenrodyellow'
ax_b = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)
ax_c = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor)
s_b = Slider(ax_b, 'b', 0.1, 18.0, valinit=b_init, valstep=delta_f)
s_c = Slider(ax_c, 'c', 0.1, 12.0, valinit=c_init)
def update(val):
b = s_b.val
c = s_c.val
l.set_ydata(((b-6*(x**k))/c)**(1/k))
fig.canvas.draw_idle()
s_b.on_changed(update)
s_c.on_changed(update)
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')
def reset(event):
s_b.reset()
s_c.reset()
button.on_clicked(reset)
def colorfunc(label):
l.set_color(label)
fig.canvas.draw_idle()
plt.show()
In case you are working with juypter notebooks you can use the widgets from ipywidgets as shown below.
Also, to get an intuition, it might help if you print out the b,c and k values.
%matplotlib inline
import numpy as np
import matplotlib.pyplot as p
from ipywidgets import *
def y(x,b,c):
k = 10/(6+c)
print(f' b={b:.3f},c={c:.3f},k={k:.3f}')
y = ((b-6*(x**k))/c)**(1/k)
return y
def inter(b0,c0):
y1=y(x,b0,c0)
p.figure(figsize=(20,6))
p.plot(x,y1)
dx=0.001
x = np.arange(0, 1.0+dx, dx) # assuming you want to go to 1 inclusively
b0=widgets.FloatSlider(value=10,min=-1,max=18.0,step=0.01,
description='b0',
continuous_update=False,
readout_format='.3f',
layout=Layout(width='90%', height='20px'))
c0=widgets.FloatSlider(value=0.1,min=-1,max=12.0,step=0.01,
description='c0',
continuous_update=False,
readout_format='.3f',
layout=Layout(width='90%', height='20px'))
interact(inter, b0=b0,c0=c0);
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
I was using this code to create an interactive plot (2d), and it works.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons
fig, ax = plt.subplots()
plt.subplots_adjust(left=0.25, bottom=0.25)
t = np.arange(0.0, 1.0, 0.001)
a0 = 5
f0 = 3
s = a0*np.sin(2*np.pi*f0*t)
l, = plt.plot(t, s, lw=2, color='red')
plt.axis([0, 1, -10, 10])
axcolor = 'lightgoldenrodyellow'
axfreq = plt.axes([0.25, 0.1, 0.65, 0.03], axisbg=axcolor)
axamp = plt.axes([0.25, 0.15, 0.65, 0.03], axisbg=axcolor)
sfreq = Slider(axfreq, 'Freq', 0.1, 30.0, valinit=f0)
samp = Slider(axamp, 'Amp', 0.1, 10.0, valinit=a0)
def update(val):
amp = samp.val
freq = sfreq.val
l.set_ydata(amp*np.sin(2*np.pi*freq*t))
fig.canvas.draw_idle()
sfreq.on_changed(update)
samp.on_changed(update)
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')
def reset(event):
sfreq.reset()
samp.reset()
button.on_clicked(reset)
rax = plt.axes([0.025, 0.5, 0.15, 0.15], axisbg=axcolor)
radio = RadioButtons(rax, ('red', 'blue', 'green'), active=0)
def colorfunc(label):
l.set_color(label)
fig.canvas.draw_idle()
radio.on_clicked(colorfunc)
plt.show()
I then tried to modify it to create an interactive 3d plot by simply changing the axes to axes3d. I added the import statement shown below and replaced the definition of "fig" and "ax" with those shown below to become 3d.
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
The plot no longer updates, and I can't figure out why. It seems that the function fig.canvas.draw_idle() doesn't work on 3d graphs, but I don't have another way of updating the graph.
Any help would be appreciated,
Thanks!
You can see that the function fig.canvas.draw_idle() does exist and works as expected by looking at the colors which become updated.
The problem lies in the set_ydata function, which works differently in 3d space.
Assuming that you want the y coordinate to update and the z-coordinate to be constant, set_data will be given the constant values, while an additional property set_3d_properties() needs to be set to control the y-coordinate.
Here is the working example code:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.widgets import Slider, Button, RadioButtons
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
plt.subplots_adjust(left=0.25, bottom=0.25)
t = np.arange(0.0, 1.0, 0.001)
### create constant z-coordinate
z = np.zeros_like(t) # <------------ here
a0 = 5
f0 = 3
s = a0*np.sin(2*np.pi*f0*t)
l, = plt.plot(t, s, lw=2, color='red')
plt.axis([0, 1, -10, 10])
axfreq = plt.axes([0.25, 0.1, 0.65, 0.03], axisbg="w")
axamp = plt.axes([0.25, 0.15, 0.65, 0.03], axisbg="w")
sfreq = Slider(axfreq, 'Freq', 0.1, 30.0, valinit=f0)
samp = Slider(axamp, 'Amp', 0.1, 10.0, valinit=a0)
def update(val):
amp = samp.val
freq = sfreq.val
#set constant z coordinate
l.set_data(t, z) # <------------ here
# set values to y-coordinate
l.set_3d_properties(amp*np.sin(2*np.pi*freq*t), zdir="y") #<------------ here
fig.canvas.draw_idle()
sfreq.on_changed(update)
samp.on_changed(update)
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')
def reset(event):
sfreq.reset()
samp.reset()
button.on_clicked(reset)
rax = plt.axes([0.025, 0.5, 0.15, 0.15], axisbg=axcolor)
radio = RadioButtons(rax, ('red', 'blue', 'green'), active=0)
def colorfunc(label):
l.set_color(label)
fig.canvas.draw_idle()
radio.on_clicked(colorfunc)
plt.show()
Recently, I am focusing on producing a time series graph. I read all the csv data to python by using pandas,which the time will be the index.
scrollbar is added as aid for presentation. I import Slider from matplotlib.widgets to produce it. And All looks fine.
However, I want to improve it. The graph now I produce is not bad. If I want to go into the datapoint I can just click the Slider bar.
However, is it possible to add a button to control the slider bar.
What I mean is that can I add a right button. Once I click this right button, the whole graph will move right 5 datapoint( or 5 minutes as it is a time series model)
And can I make a 'left' button too?
Therefore, if I will go fast I can direct click the Slider bar. Then, If i wanna present it slow, I can just click the button?
Actually, I have read some information about 'button library'. However, I don't know how to use it to update the Slider.
Here is my code
df_BA = cs_66667_BS[['BidPrice','AskPrice']]
title_name = 'cs_66667_BS'
ax = df_BA.plot(title= title_name, fontsize= 10, figsize=(20, 10))
ax.set_xlabel('Date', fontsize= 15)
ax.set_ylabel('Price', fontsize= 15)
plt.subplots_adjust(left= 0.04, bottom= 0.25, right= 0.99, top= 0.95, wspace= 0, hspace= 0.1 )
x= df_BA.index.to_pydatetime()
x_min_index = 0
x_max_index = 1
x_min = x[0]
x_max = x[-1] - datetime.timedelta(minutes=4)
y_min = df_BA.BidPrice.min()
y_max = df_BA.AskPrice.max()
# timedelta
x_dt = datetime.timedelta(minutes=5)
axcolor = 'lightgoldenrodyellow'
axpos = plt.axes([0.05, 0.1, 0.9, 0.05], axisbg=axcolor)
slider_max = len(x) - x_max_index - 1
# Slider(axes, name, min, max)
spos = Slider(axpos, 'Pos', matplotlib.dates.date2num(x_min), matplotlib.dates.date2num(x_max))
# pretty date names
plt.gcf().autofmt_xdate()
def update(val):
pos = spos.val
xmin_time = matplotlib.dates.num2date(pos)
xmax_time = matplotlib.dates.num2date(pos) + x_dt
xmin_time = pos
ax.axis([xmin_time, xmax_time, y_min, y_max])
########################################################
fig.canvas.draw_idle()
spos.on_changed(update)
plt.show()
You can easily add a matplotlib.widgets.Button to the plot, in the same way you added a matplotlib.widgets.Slider.
You can then connect the button to a function forward() or backward() which will set the Slider to a new position.
Here is a complete working example which is hopefully close enough to your's (I did change some stuff though)
import matplotlib.pyplot as plt
import matplotlib.dates
from matplotlib.widgets import Slider, Button
import datetime
import pandas as pd
import numpy as np
rng = pd.date_range(datetime.datetime.now(), periods=340, freq='T')
data = {'BidPrice': np.random.random(340)*np.sin(np.arange(340)/30.),
'AskPrice': np.random.random(340)*np.sin(np.arange(340)/30.-2) }
df_BA = pd.DataFrame(data, columns=['BidPrice','AskPrice'], index=rng)
ax = df_BA.plot(title= "Title", fontsize= 10, figsize=(10, 8))
ax.set_xlabel('Date', fontsize= 15)
ax.set_ylabel('Price', fontsize= 15)
plt.subplots_adjust(left= 0.04, bottom= 0.25, right= 0.99, top= 0.95, wspace= 0, hspace= 0.1 )
x= df_BA.index.to_pydatetime()
x_min = x[0]
x_max = x[0] + datetime.timedelta(minutes=20)
y_min = df_BA.BidPrice.min()
y_max = df_BA.AskPrice.max()
ax.set_xlim([x_min, x_max])
ax.set_ylim([y_min, y_max])
x_dt = datetime.timedelta(minutes=10)
axpos = plt.axes([0.2, 0.1, 0.58, 0.03], axisbg='w')
axpos1 = plt.axes([0.05, 0.1, 0.05, 0.03], axisbg='w')
axpos2 = plt.axes([0.93, 0.1, 0.05, 0.03], axisbg='w')
# Slider(axes, name, min, max)
spos = Slider(axpos, 'Pos', matplotlib.dates.date2num(x_min), matplotlib.dates.date2num(x[-1]))
spos.set_val(matplotlib.dates.date2num(x[0]))
button1 = Button(axpos1, '<', color='w', hovercolor='b')
button2 = Button(axpos2, '>', color='w', hovercolor='b')
def update(val):
pos = spos.val
xmin_time = matplotlib.dates.num2date(pos)
xmax_time = matplotlib.dates.num2date(pos) + x_dt
ax.set_xlim([xmin_time, xmax_time ])
plt.gcf().canvas.draw_idle()
def forward(vl):
pos = spos.val
spos.set_val(matplotlib.dates.date2num( matplotlib.dates.num2date(pos) + datetime.timedelta(minutes=5) ))
def backward(vl):
pos = spos.val
spos.set_val(matplotlib.dates.date2num( matplotlib.dates.num2date(pos) - datetime.timedelta(minutes=5) ))
spos.on_changed(update)
button1.on_clicked(backward)
button2.on_clicked(forward)
plt.show()