Animated matplotlib imshow - python

Let me first clarify that I'm not trying to generate random walk lines like in this and many other questions. I'm trying to make a random walk heat map that changes color as points are revisted, like this.
I've been able to create still-lifes like this: but I want to see the process.
I can get the figure to show up, and if I print the array at each step I can see that the walk is working. But the figure itself doesn't animate. My code:
import matplotlib as mpl
from matplotlib import pyplot as plt
from matplotlib import animation as anim
import numpy as np
import sys
import random
length = int(sys.argv[1])
fig = plt.figure()
ax = plt.axes(xlim=(0, length-1), ylim=(0, length-1))
arr = np.zeros((length, length), dtype = int)
cmap = mpl.colors.LinearSegmentedColormap.from_list('my_colormap',
['black','green','white'],
256)
bounds=[0,0,10,10]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
im=plt.imshow(arr, interpolation='nearest',
cmap = cmap,
origin='lower')
x = int(np.random.random_sample() * length)
y = int(np.random.random_sample() * length)
def walk():
global x, y
rand = np.random.random_sample()
if rand < 0.25 :
if x == length - 1:
x = 0
else: x = x + 1
elif rand < 0.5 :
if x == 0:
x = length - 1
else: x = x - 1
elif rand < 0.75 :
if y == length - 1:
y = 0
else: y = y + 1
else:
if y == 0:
y = length - 1
else: y = y - 1
return
def stand(arr):
global x,y
arr[x][y] = arr[x][y] + 1
return arr
def animate(i):
arr=im.get_array()
walk()
#print(a)
arr = stand(arr)
im.set_array(arr)
return [im]
anim = anim.FuncAnimation(fig, animate, frames=200, interval=20, blit=True)
plt.show()
Running Python 3.6, as you can see by the print.
There are so many videos with these animated grids and I can't find any answers! Somebody must know how to do it. Thanks!

I added animated=True and vmin=0, vmax=255, in the imshow() function below. I also changed the stand() line to arr[x][y] = arr[x][y] + 10.
#!/usr/bin/env python3
import matplotlib as mpl
from matplotlib import pyplot as plt
from matplotlib import animation as anim
import numpy as np
import sys
import random
length = int(sys.argv[1])
fig = plt.figure()
ax = plt.axes(xlim=(0, length-1), ylim=(0, length-1))
arr = np.zeros((length, length), dtype = int)
cmap = mpl.colors.LinearSegmentedColormap.from_list('my_colormap',
['black','green','white'],
256)
bounds=[0,0,10,10]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
im=plt.imshow(arr, interpolation='nearest',
cmap = cmap, vmin=0, vmax=255,
origin='lower', animated=True) # small changes here
x = int(np.random.random_sample() * length)
y = int(np.random.random_sample() * length)
def walk():
global x, y
rand = np.random.random_sample()
if rand < 0.25 :
if x == length - 1:
x = 0
else: x = x + 1
elif rand < 0.5 :
if x == 0:
x = length - 1
else: x = x - 1
elif rand < 0.75 :
if y == length - 1:
y = 0
else: y = y + 1
else:
if y == 0:
y = length - 1
else: y = y - 1
return
def stand(arr):
global x,y
arr[x][y] = arr[x][y] + 1000
return arr
def animate(i):
global x,y
arr=im.get_array()
walk()
#print(a)
arr = stand(arr)
im.set_array(arr)
return [im]
anim = anim.FuncAnimation(fig, animate, frames=200, interval=20, blit=True)
plt.show()
And I ran it with length = 50 and I get an animation. See it here. So you may have to play around with your color choices a bit.

Related

When plotting a np.linspace the figure labeling is not what I wanted

I want to animate julia sets. Anyways everything works so far,
I only need to change the axis labeling. When plotting my values the
x- and y axis are both showing the 500x500 linspace. Id rather like to see
the [-1,1]x[-1,1] Intervall ive defined the linspace on. How could I change that?
thank you :)
code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# Parameters and Test-Function
f = lambda z: z ** 2 - 0.1+0.651*1j
N = 1000
R = 2
def pre_greyscale(f, c, N, R):
if np.abs(c) > R:
return 0
else:
for i in range(0, N):
c = f(c)
if np.abs(c) > R:
return i + 1
return N
# fig1 = plt.figure()
real = np.linspace(-1, 1, num=500)
imaginary = np.linspace(-1, 1, num=500)
pre_image = np.empty(shape=(real.size, imaginary.size))
for k, r in enumerate(real):
for p, i in enumerate(imaginary):
pre_image[p, k] = pre_greyscale(f, r + i * 1j, N, R)
def animate(m):
image = np.empty(shape=(real.size, imaginary.size))
for k in range(0, 500):
for p in range(0, 500):
if pre_image[p, k] <= m:
image[p, k] = 1 - pre_image[p, k] / m
# else:
# image[k, p] = 0
# mat = plt.imshow(image, cmap='gray')
# plt.show()
return image
imagelist = [animate(x) for x in range(N)]
fig = plt.figure() # make figure
# Initialize imshow
im = plt.imshow(imagelist[0], cmap=plt.get_cmap('gray'), vmin=0, vmax=1)
# function to update figure
def updatefig(j):
# set the data in the axesimage object
im.set_array(imagelist[j])
# return the artists set
return [im]
# kick off the animation
ani = FuncAnimation(fig, updatefig, frames=N,
interval=20, blit=True)
ani.save('fractal2.gif', writer='pillow')
Adding the extent parameter to plt.imshow will set the correct labels:
# Initialize imshow
im = plt.imshow(imagelist[0], cmap=plt.get_cmap('gray'), vmin=0, vmax=1, extent=[-1,1,-1,1])

How to correct this error: OverflowError: cannot convert float infinity to integer

I am trying to make a Bak-Tang sandpile model, and I am getting this error. I am making a python code for Plot distribution. I want to generate the plots of Avalanche and then fit it with a linear function. I have provided the full code which show the error. This is my code:
import numpy as np
import random
import matplotlib.pyplot as plt
import sys
sys.setrecursionlimit(100000)
import warnings
warnings.filterwarnings("ignore")
iterations = 100000 # number of iterations
N = 50 # size of matrix
global threshold
threshold = 4 # critical level for toppling to occur
topple = 0
topplesize = []
def initializeMatrix(N):
return np.random.randint(4, size=(N,N))
def drop(M, x, y, N):
global topple
withinBounds = True
if x < 0 or x > N-1 or y < 0 or y > N-1:
withinBounds = False
pass
if withinBounds:
M[x,y] = M[x,y] + 1
if M[x,y] >= threshold:
M[x,y] = M[x,y] - 4 # reset the cell value and distribute to neighbors
topple += 1 # count the toppling
drop(M, x+1, y, N)
drop(M, x-1, y, N)
drop(M, x, y-1, N)
drop(M, x, y+1, N)
M = initializeMatrix(N)
plt.figure(figsize=(8,8))
plt.imshow(M)
plt.colorbar()
plt.title("The Sandpile Table")
plt.show()
for i in range(iterations):
topple = 0
x = random.randint(0, N-1)
y = random.randint(0, N-1)
drop(M, x, y, N)
topplesize.append(topple)
x, y = np.histogram(topplesize, 1000)
plt.figure(figsize=(10,7))
plt.clf()
plt.loglog(y[0:-1],x, 'r.')
plt.title("Avalanche Size Distribution", fontsize=14)
plt.xlabel(r"$\log$" + "(Avalanche Size)", fontsize=12)
plt.ylabel(r"$\log$" + "(Frequency)", fontsize=12)
plt.show()
plt.figure(figsize=(8,8))
plt.imshow(M)
plt.colorbar()
plt.title("The Sandpile Table")
plt.show()
import powerlaw
def plotDist(topplesize, xmin, xmax):
x, y = powerlaw.pdf(np.array(topplesize), linear_bins=True)
ind = y>0
y = y[ind]
x = x[:-1]
x = x[ind]
tsize = np.array(topplesize)
fit = powerlaw.Fit(tsize[tsize>0], discrete=True, xmin=xmin, xmax=xmax)
alpha = fit.power_law.alpha
fig = plt.figure(figsize=(10,7))
ax1 = fig.add_subplot(1,1,1)
#ax1.scatter(x, y, color='r', s=1.2, label="PDF (scatter)")
powerlaw.plot_pdf(tsize[tsize>0], color='b', ax=ax1, linestyle='-', linewidth=2, label="PDF")
fit.power_law.plot_pdf(tsize[tsize>0], color='b', ax=ax1, linestyle='--', linewidth=1, label="Power-law fit")
plt.xlabel(r"Avalanche Size, $s$", fontsize=12)
plt.ylabel(r"$p(s)$", fontsize=12)
_ = plt.text(10e1,50e-4,r"$\alpha$="+str(round(alpha,1)), fontsize=13)
_ = plt.legend(frameon=False)
return
topplesize50 = topplesize
plotDist(topplesize50, xmin=10, xmax=500)
When I am trying to run plotDist(), it gives me error. like this:
OverflowError Traceback (most recent call last)
<ipython-input-40-0a16409c5a3d> in <module>()
----> 1 plotDist(topplesize60, xmin=10, xmax=1000)
1 frames
<ipython-input-36-8b425d5f67b8> in plotDist(topplesize, xmin, xmax)
1 import powerlaw
2 def plotDist(topplesize, xmin, xmax):
----> 3 x, y = powerlaw.pdf(np.array(topplesize), linear_bins=True)
4 ind = y>0
5 y = y[ind]
/usr/local/lib/python3.6/dist-packages/powerlaw.py in pdf(data, xmin, xmax, linear_bins, **kwargs)
1962 bins = kwargs.pop('bins')
1963 elif linear_bins:
-> 1964 bins = range(int(xmin2), ceil(xmax2)+1)
1965 else:
1966 log_min_size = log10(xmin2)
OverflowError: cannot convert float infinity to integer

Catch error in update function of matplotlib animation

I'd like to be able to catch an error while plotting using the matplotlib animation function.
This is necessary for me as I have a program where it can happen that an error occurs in the updatefig function after a couple of loops. I'd like to then continue in the script to save all the data generated up to that point.
Instead of throwing an error, running the code below will just lead to the following output:
Process finished with exit code 1
I tried to put the try except clause at all positions I could think of but was never able to go to the last print().
See this MWE (taken from here):
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
def f(x, y):
return np.sin(x) + np.cos(y)
x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)
counter = 0
im = plt.imshow(f(x, y), animated=True)
def updatefig(*args):
global x, y, counter
x += np.pi / 15.
y += np.pi / 20.
im.set_array(f(x, y))
counter += 1
# do something that might fail at one point (and will fail in this example)
if counter > 10:
b = 0
print('bla ' + b) # error
return im,
ani = animation.FuncAnimation(fig, updatefig, interval=50, blit=True)
plt.show()
print('do other stuff now, e.g. save x and y')
There is an error because you are attempting to concatenate a string with an int:
Option 1:
correct the error:
import matplotlib
matplotlib.use('TkAgg')
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
def f(x, y):
return np.sin(x) + np.cos(y)
x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)
counter = 0
im = plt.imshow(f(x, y), animated=True)
def updatefig(*args):
global x, y, counter
x += np.pi / 15.
y += np.pi / 20.
im.set_array(f(x, y))
counter += 1
# do something that will not fail
if counter > 10:
b = 0
print('bla ' + str(b))
return im,
ani = animation.FuncAnimation(fig, updatefig, interval=50, blit=True)
plt.show()
print('do other stuff now, e.g. save x and y')
option 2:
catch the Error, save the data, and continue:
import matplotlib
matplotlib.use('TkAgg')
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
def f(x, y):
return np.sin(x) + np.cos(y)
x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)
counter = 0
im = plt.imshow(f(x, y), animated=True)
def save_if_error():
print('do other stuff now, e.g. save x and y')
def updatefig(*args):
global x, y, counter
x += np.pi / 15.
y += np.pi / 20.
im.set_array(f(x, y))
counter += 1
# do something that might fail at one point and catch the error, save the data and continue
if counter > 10:
b = 0
try:
print('bla ' + b) # error
except TypeError:
print("save the data here")
save_if_error()
return im,
ani = animation.FuncAnimation(fig, updatefig, interval=50, blit=True)
plt.show()

Displaying array values in contour plots

I have an array A which contains values that I plot using X and Y as the coordinate axes, using
plt.contourf(X,Y,A)
I'd like to know how I could obtain the values of A when I hover my cursor over a certain (X,Y) point in the plot, or any other alternative to this where I could obtain the value at any point while I am viewing the plot.
Thanks a lot!
You have to use format_coord property of axis object:
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
A = np.arange(25).reshape(5,5)
X = np.arange(5)
Y = np.arange(5)
X,Y = np.meshgrid(X,Y)
plt.contourf(X,Y,A)
nrows, ncols = A.shape
def format_coord(x, y):
i = int(x)
j = int(y)
if j >= 0 and j < ncols and i >= 0 and i < nrows:
return "A[{0}, {1}] = {2}".format(i, j, A[i][j])
else: return "[{0} {1}]".format(i, j)
ax.format_coord = format_coord
plt.show()
Example:

how to remove plot elements in python

I'm trying to a 3d figure without any plot elements in python. Sort of a 3d version of this.
When I run the code I have added below, I get regular plot.
I want to remove the axes, axes labels, ticks, and background (and remain only with the surface).
How can I remove them.
Also, is there way to add arrows to the plot?
Here is my code:
import random
import math
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
pi = 3.14159
bignum = 3
amp = 0.1
mat = []
X = []
Y = []
class mode:
i=0
j=0
phase=0
amp=0
modes = dict()
for i in range(0,3):
submodes = dict()
for j in range (0,5):
if not (i==0 and j==2):
m = mode()
m.i = i
m.j = j
m.amp = amp*random.random()/(pow(i,2) + pow(j-2,2))
m.phase = random.random()*2*pi
submodes[j] = m
modes[i] = submodes
for x in range (0,bignum):
mat.append([])
for y in range (0,bignum):
dz = 0
for i in range (0,3):
for j in range (0,5):
if not (i == 0 and j == 2):
dz += math.cos(i*x*2*pi/bignum + j *y*2/bignum + modes[i][j].phase)*modes[i][j].amp
mat[x].append(dz)
X = np.mgrid[:bignum,:bignum]
print (len(X[0]))
print (len(mat))
fig = plt.figure(figsize=plt.figaspect(2.))
fig.frameon=True
ax = fig.add_subplot(1,1,1, projection='3d')
ax.frameon=False
ax.xticks=[]
ax.yticks=[]
ax.zticks=[]
surf = ax.plot_surface(X[0],X[1],mat,rstride=1, cstride=1,
linewidth=0, antialiased=False)
ax.set_zlim3d(0, 1)
plt.show()
To eliminate the 3d frame from the figure, use:
ax.set_axis_off()
This doesn't answer much of your question. But you can start turning stuff off with
plt.setp(ax.get_xticklabels(), visible=False)
I did some of it below. Also, the plt.annotate() function is how to add arrows in 2d plots...not sure how it upscales.
import random
import math
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
pi = 3.14159
bignum = 3
amp = 0.1
mat = []
X = []
Y = []
class mode:
i=0
j=0
phase=0
amp=0
modes = dict()
for i in range(0,3):
submodes = dict()
for j in range (0,5):
if not (i==0 and j==2):
m = mode()
m.i = i
m.j = j
m.amp = amp*random.random()/(pow(i,2) + pow(j-2,2))
m.phase = random.random()*2*pi
submodes[j] = m
modes[i] = submodes
for x in range (0,bignum):
mat.append([])
for y in range (0,bignum):
dz = 0
for i in range (0,3):
for j in range (0,5):
if not (i == 0 and j == 2):
dz += math.cos(i*x*2*pi/bignum + j *y*2/bignum + modes[i][j].phase)*modes[i][j].amp
mat[x].append(dz)
X = np.mgrid[:bignum,:bignum]
print (len(X[0]))
print (len(mat))
fig = plt.figure(figsize=plt.figaspect(2.))
fig.frameon=True
ax = fig.add_subplot(1,1,1, projection='3d')
ax.frameon=False
surf = ax.plot_surface(X[0],X[1],mat,rstride=1, cstride=1,
linewidth=0, antialiased=False)
ax.set_zlim3d(0, 1)
plt.setp(ax.get_xticklabels(), visible=False)
plt.setp(ax.get_yticklabels(), visible=False)
plt.setp(ax.get_zticklabels(), visible=False)
plt.setp(ax.get_xticklines(), visible=False)
plt.setp(ax.get_yticklines(), visible=False)
plt.setp(ax.get_zticklines(), visible=False)
plt.setp(ax.get_frame(), visible = False)
#plt.annotate(r'Hello', xy = (.5, .5),
# xytext = (10,10),
# textcoords='offset points', arrowprops=dict(arrowstyle='->',
# connectionstyle='arc3,rad=0'))
plt.show()
You didn't ask this...but you should vectorize this code. Most/(all?) of the for loops could be avoided.

Categories