I'm in doubt about what is the difference among the codes below. I'm using matplotlib's animation class to render numpy's arrays. In the atualizaMundo() function, if I use mundo[:] = new_mundo[:] it works just fine, but if I use mundo=new_mundo the arrays get equal but the animation doesn't work. What is the difference here?
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
ON = 255
OFF = 0
def criaMundo(N):
return(np.random.choice([ON,OFF],N*N,p=[0.5,0.5]).reshape(N,N))
def atualizaMundo(frameNum,N,mundo,img):
new_mundo = np.random.choice([ON,OFF],N*N,p=[0.5,0.5]).reshape(N,N)
img.set_data(mundo)
mundo[:]=new_mundo[:]
#mundo=new_mundo
return(img,)
def main():
try:
N = 4
mundo = criaMundo(N)
print(mundo)
fig1,ax = plt.subplots()
img = ax.imshow(mundo)
animacao = animation.FuncAnimation(fig1, atualizaMundo, fargs=(N,mundo,img,), blit=True)
plt.show()
except Exception as ex:
pass
if __name__ == '__main__':
try:
main()
except Exception as fk:
pass
The line mundo[:]=new_mundo[:] modifies the existing array mundo. You're therefore always operating on the same object and changes made to it are reflected in the animation. Next time the function is called by the animation the same object is passed as argument so the changes made in the previous call are preserved. Note that mundo[:]=new_mundo[:] is equivalent to mundo[:]=new_mundo.
Opposed to that mundo=new_mundo assigns the new array to a local variable called mundo, which replaces the passed argument mundo. However, it is only of local scope and once the function finishes, the changed mundo is simply not present anymore. In the next call to the function, the old and unchanged mundo is passed again to the function, leading to a static animation.
It should be noted that you don't actually need to pass mundo at all in this case, as you could simply set the newly calculated array new_mundo directly to the image: img.set_data(new_mundo).
Related
I'm working on a program that displays drawings, with the option of animating the drawing to show which order the lines should be drawn in. When I used plt.show() to display the drawing as a still image, everything works as expected: the code pauses, and then resumes again as soon as the popup window is closed. However, when I use the same function to display an animated drawing, the code remains frozen even after I close the popup window. The only way to get it unstuck is to fully restart the python shell - it doesn't respond to KeyboardInterrupt.
UPDATE:
When I set repeat to False in the FuncAnimation call, it behaves slightly differently. If I close the popup window while the animation is running, the glitch happens, locking up my program. However, if I close the popup after the animation has finished, the program continues as intended. It seems like the glitch here has something to do with closing the window before the animation is done.
UPDATE 2:
For some reason, replacing all of the plt.plot() calls in the animate_pattern function with ax.plot() fixes the issue. I have no idea why this works, because as far as I know the two functions do the same thing. However, the problem is solved.
Below is the code for the module that handles the animation. Some notes:
Normally, I create the animations by calling plot_animated() from a different module. However, the bug happens whether or not I create the animation that way or do it through the code in this module's if name == main statement.
convert_to_points() is a function from the main module that turns the data it's given into a list of x-values and a list of y-values to be plotted.
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation,PillowWriter
from functools import partial
from os.path import isfile
import json
end_marker = [None]
smooth = 40
def animate_pattern(f,anim_data):
x_anim,y_anim,scale = anim_data
global end_marker
# starting point
if x_anim[f] is None:
end_marker = plt.plot(x_anim[1],y_anim[1],marker='o',ms=1.8*scale,mew=0.4*scale,mec="black",c="#ff6bff")
# segment going into a point
if f%smooth == 1:
plt.plot(x_anim[f-1:f+1],y_anim[f-1:f+1],c="#ff6bff",lw=scale)
plt.plot(x_anim[f],y_anim[f],marker='o',ms=1.8*scale,mew=0.4*scale,mec="black",c="#ff6bff")
# segment coming out of a point
elif f%smooth in (2,4):
plt.plot(x_anim[f-1:f+1],y_anim[f-1:f+1],c="#ff6bff",lw=scale)
plt.plot(x_anim[f-f%smooth+1],y_anim[f-f%smooth+1],marker='o',ms=1.8*scale,mew=0.4*scale,mec="black",c="#ff6bff")
# all other segments
else:
plt.plot(x_anim[f-1:f+1],y_anim[f-1:f+1],c="#ff6bff",lw=scale)
# marker for current endpoint of animated line
if x_anim[f]:
end_marker[0].remove()
end_marker = plt.plot(x_anim[f],y_anim[f],marker='h',ms=2.4*scale,mew=0.5*scale,mec="#547dd6",c="#6bc9e8")
def init_pattern(plot_data,settings):
x_vals,y_vals,scale = plot_data[:3]
# clear the canvas
plt.cla()
plt.gca().axis("off")
# draw the full pattern in the background
for i in range(len(x_vals)-1):
plt.plot(x_vals[i:i+2],y_vals[i:i+2],color=settings["monochrome_color"],lw=scale)
plt.plot(x_vals[i],y_vals[i],'ko',ms=2*scale)
plt.plot(x_vals[-1],y_vals[-1],'ko',ms=2*scale)
def anim_interpolate(plot_data):
x_vals,y_vals,scale = plot_data[:3]
x_anim,y_anim = [None],[None]
# create five interpolated points after each point
for i in range(len(x_vals)-1):
x_dist = x_vals[i+1] - x_vals[i]
y_dist = y_vals[i+1] - y_vals[i]
x_anim += [x_vals[i]+x_dist*(1/smooth)*j for j in range(smooth)]
y_anim += [y_vals[i]+y_dist*(1/smooth)*j for j in range(smooth)]
# add the last point
x_anim.append(x_vals[-1])
y_anim.append(y_vals[-1])
return x_anim,y_anim,scale
def plot_animated(plot_data,settings,):
# convert basic pointlist into special version for animating
anim_data = anim_interpolate(plot_data)
# create animation object by repeatedly invoking animate_pattern()
ani = FuncAnimation(plt.gcf(),
func=animate_pattern,
fargs=[anim_data],
frames=len(anim_data[0]),
init_func=partial(init_pattern,plot_data,settings),
interval=1000/smooth,
repeat=True)
return ani
if __name__ == "__main__":
with open("settings.json",mode="r") as file:
settings = json.load(file)
from hex_draw import convert_to_points
print("Displaying test animation...")
plot_data = convert_to_points("qeewdweddw","northeast",settings)
ax = plt.figure(figsize=(4,4)).add_axes([0,0,1,1])
ax.set_aspect("equal")
ani = plot_animated(plot_data,settings)
plt.show()
I'm trying to use ginput to register clicks on a map, and wanted to add action buttons using matplotlib widgets. In the following code, I can pass back the value of action to the main code by declaring it a global. If I click on the map, action=0, if I click on the button, action=1, as desired.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
class Index:
def test(self, event):
global action
action=1
# fake data
x=np.arange(30)
y=x**2
fig,ax=plt.subplots()
ax.plot(x,y)
callback = Index()
buttonname=['test']
colors=['white']
idx=[0.2]
bax,buttons={},{}
# set up list of buttons.
for i,col,button in zip(idx,colors,buttonname):
bax[button] = plt.axes([0.92, i, 0.07, 0.07])
buttons[button] = Button(bax[button],button,color=col,hovercolor='green')
buttons[button].on_clicked(getattr(callback,button))
# register click on plot
while True:
pts=plt.ginput(1)
plt.pause(0.5)
print("action is ",action)
action=0 # reset
But my confusion is, if I take the exact same code and place it in a def block, the value of action is no longer passed back, action is always zero.
def subtest():
class Index:
def test(self, event):
global action
action=1
# fake data
action=0
x=np.arange(30)
y=x**2
fig,ax=plt.subplots()
ax.plot(x,y)
callback = Index()
buttonname=['test']
colors=['white']
idx=[0.2]
bax,buttons={},{}
# set up list of buttons.
for i,col,button in zip(idx,colors,buttonname):
bax[button] = plt.axes([0.92, i, 0.07, 0.07])
buttons[button] = Button(bax[button],button,color=col,hovercolor='green')
buttons[button].on_clicked(getattr(callback,button))
# register click on plot
while True:
pts=plt.ginput(1)
plt.pause(0.5)
print("action is ",action)
action=0 # reset
res=subtest()
I'm very confused as to why this happens. I tried moving the class definition out into the main code but that didn't help. I'm happy for any kind of solution (e.g. passing action through an argument, which I have not understood how to do with widgets), as I think that the use of global is often frowned apon. But also a global -based solution is fine.
action inside subtest is local to subtest, while action inside Index.test is global. Either declare action global in subtest, or use nonlocal in Index.test.
(I suspect there may be better solutions without globals, but since I'm not familiar with the GUI toolkit I'll leave that to someone else.)
you want to check out this article on closures and this other article on closures. I think you need to declare action outside of both your class and functions and then reference it with global in the class.
I don't think you need to use a class - you can get away with passing a variable, boolean, dictionary around functions and achieve the same thing.
You can use the "on_click" event to do what you want see this widget tutorial widget tutorial and this on matplotlib event handling
Here is an example piece of code using "global" to effect state changes. Better to pass around a variable or use the event to trigger whatever you want the state to effect.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
state = False
def change_state(btn_action):
#do something
btn_action= not(btn_action)
return btn_action
def grid(val):
global state
#do something
state =change_state(state)
print(state)
ax.grid()
fig.canvas.draw()
#reset value
state =change_state(state)
print(f'resetting state to {state}')
# fake data
x=np.arange(30)
y=x**2
#create fig, ax
fig =plt.figure()
ax= fig.subplots()
p, = ax.plot(x,y)
# set up list of buttons.
buttonname=['test']
colors=['white']
idx=[0.2]
bax,buttons={},{}
for i,col,button in zip(idx,colors,buttonname):
bax[button] = plt.axes([0.92, i, 0.07, 0.07])
buttons[button] = Button(bax[button],button,color=col,hovercolor='green')
buttons[button].on_clicked(grid)
#show plot
fig.canvas.draw()
plt.show()
I am trying to do an exponential smothing in Python on some detrended data on a Jupyter notebook. I try to import
from statsmodels.tsa.api import ExponentialSmoothing
but the following error comes up
ImportError: cannot import name 'SimpleExpSmoothing'
I don't know how to solve that problem from a Jupyter notebook, so I am trying to declare a function that does the exponential smoothing.
Let's say the function's name is expsmoth(list,a) and takes a list list and a number a and gives another list called explist whose elements are given by the following recurrence relation:
explist[0] == list[0]
explist[i] == a*list[i] + (1-a)*explist[i-1]
I am still leargnin python. How to declare a function that takes a list and a number as arguments and gives back a list whose elements are given by the above recurrence relation?
A simple solution to your problem would be
def explist(data, a):
smooth_data = data.copy() # make a copy to avoid changing the original list
for i in range(1, len(data)):
smooth_data[i] = a*data[i] + (1-a)*smooth_data[i-1]
return smooth_data
The function should work with both native python lists or numpy arrays.
import matplotlib.pyplot as plt
import numpy as np
data = np.random.random(100) # some random data
smooth_data = explist(data, 0.2)
plt.plot(data, label='orginal')
plt.plot(smooth_data, label='smoothed')
plt.legend()
plt.show()
I have a question about using fig, axes function in a loop in matplotlib.
I am trying to create a few plots with multiple subplots (the number of subplots isn not fixed) in a loop as follows:
def start_plot(self):
if self.running:
run_fig, run_ax = plt.subplots(*self.matrix)
if self.histogram:
hist_fig, hist_ax = plt.subplots(*self.matrix)
def create_signal_plots(self, iwindow, window_name):
if self.running:
run_ax[iwindow+1].plot(running_perf, label=window_name) # throws error run_ax not recognized
if self.histogram:
hist_ax[iwindow+1].hist(timeseries, label=window_name)
plot = plot_class(run =1, hist =1, matrix = get_matrix(*args)) # e.g. matrix = (3,2)
for istrat, strat in enumerate(strats):
plot.start_plot()
for iwindow, window in enumerate(windows):
plot.create_plots(iwindow, window)
Is there a way to make this work without having to return axes in function and pass it around? If I use plt.figure instead of fix,axes, then i can simply update any figure using plt.figure(fig_no).
You can store run_fig and run_ax in your object as instance attributes and then access them from any other method of that object. This would be done using self.
Use self.run_fig, etc. in start_plot and create_signal_plots as in:
def start_plot(self):
if self.running:
self.run_fig, self.run_ax = plt.subplots(*self.matrix)
if self.histogram:
self.hist_fig, self.hist_ax = plt.subplots(*self.matrix)
def create_signal_plots(self, iwindow, window_name):
if self.running:
self.run_ax[iwindow+1].plot(running_perf, label=window_name) # throws error run_ax not recognized
if self.histogram:
self.hist_ax[iwindow+1].hist(timeseries, label=window_name)
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
I am wrtiting a program with Python 3.4.1 to analyze a certain type of captcha.
Here's the part where I have the problem.
def f(path):
i = Image.open(path)
a = np.array(i) #to transform the image into an array using numpy
b = combinator(a) #this is a function I created to process the image (thresholding...)
capreader(b) #this is a function that divides the array and recognizes the character in each segment (I created previously functions for each character)
now when I call f() for a path 'p' it gives me a certain result (which is wrong). And when I call each of the instructions inside f() individually it gives me an other result (which is correct):
i = Image.open('p')
ia = np.array(i)
ib = combinator(ia)
capreader(ib)
This is weird! because I think that logically they should give the same result.
I then tried to see if inside f(), the array b is the same if it isn't inside f():
def x(path):
i = Image.open(path)
a = np.array(i)
b = combinator(a)
print(np.array_equal(b,ib)
and the result was False.
I then tested this:
def y(path):
i = Image.open(path)
a = np.array(i)
b = combinator(a)
plt.imshow(b)
plt.show()
capreader(b)
this time (after I close the pyplot window) capreader() gives me the correct answer!!
so I continued the testing, this time with this:
def test(path):
a = Image.open(path)
b = np.array(a)
c = combinator(b)
print(np.array_equal(c,ib)) #False
plt.imshow(c)
plt.show()
print(np.array_equal(c,ib)) #True
capreader(c)
I don't understand what's happening and how I can solve it. obviously what's making the difference here are the function from plt between the two comparisons which give opposing results(False, True). I read on the internet that plt.show() is a blocking function. I don't know what that means, but I put it in here in case it helps solving the case.