Good morning all,
I have been working on a small personal project, but am now stuck in the final plotting phase.
So far, I have created a 4 dimensional array called "colored_map":
dimension 1: frame number (0-499) which I'm trying to iterate over
dimension 2 and 3: x and y positions
dimension 4: RGB values of each element
In short, I am trying to plot each frame by iterating over the first dimension of the array, and clear the plot after each iteration so the output looks like a "video".
I have tried the following piece of code, but it does not replace/ overwrite the previous fig:
for j in range(0,500):
img = colored_map[j]
fig = plt.figure(figsize = (5,5))
#ax = fig.add_subplot(111)
#ax.set_axis_off()
plt.clf()
plt.imshow(img)
plt.pause(1)
I also tried the below piece of code which I found somewhere else, but it only creates the first frame:
#Setting up the figure and axes
fig = plt.figure(figsize=(5,5))
ax = fig.add_subplot(111)
ax.set_axis_off()
im = ax.imshow(colored_map[0])
#Animate function to iterate over successive frames
def animate(i):
im.set_data(colored_map[i])
return im
#Calling the animator to put the different frames together
anim = animation.FuncAnimation(fig, animate, frames=500, interval=20)
plt.show()
You can create the colored_map array using the following piece of code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation, colors
density = 0.8
prob = 0.9
#You could reduce n_iterations (= 1st dim) and forest_size (2nd and 3rd dim) to create an smaller array
n_iterations = 200
forest_size = [101,101]
black = [0,0,0]
green = [0,153,0]
orange = [255,128,0]
states = np.zeros((n_iterations,forest_size[0],forest_size[1]))
states[0] = np.random.choice([0,1],forest_size,p=[1-density, density])
states[0,np.random.randint(1,forest_size[0]-2),np.random.randint(1,forest_size[1]-2)] = 2
for i in range(1,n_iterations):
states[i]=states[i-1].copy()
for x in range(1, forest_size[0]-1):
for y in range(1, forest_size[1]-1):
if states[i-1,x,y]==2:
states[i,x,y]=0
states[i,x+1,y]= np.random.choice([1,2],1,p=[1-prob,prob])[0]
if states[i-1,x-1,y]==1:
states[i,x-1,y]= np.random.choice([1,2],1,p=[1-prob,prob])[0]
if states[i-1,x,y+1]==1:
states[i,x,y+1]= np.random.choice([1,2],1,p=[1-prob,prob])[0]
if states[i-1,x,y-1]==1:
states[i,x,y-1]= np.random.choice([1,2],1,p=[1-prob,prob])[0]
colored_map = np.zeros((n_iterations,forest_size[0],forest_size[1],3),dtype=np.uint8)
for i in range(0,colored_map.shape[0]):
for x in range(0,colored_map.shape[1]):
for y in range(0,colored_map.shape[2]):
cur_value = states[i,x,y].copy()
if cur_value == 0:
colored_map[i,x,y] = black
if cur_value == 1:
colored_map[i,x,y] = green
if cur_value == 2:
colored_map[i,x,y] = orange
Can someone please help me by pointing out what I'm doing wrong here?
Thank you in advance for your help!
Related
I have 2 lists of figures and their axes.
I need to plot each figure in a single subplot so that the figures become in one big subplot. How can I do that?
I tried for loop but it didn't work.
Here's what I have tried:
import ruptures as rpt
import matplotlib.pyplot as plt
# make random data with 100 samples and 9 columns
n_samples, n_dims, sigma = 100, 9, 2
n_bkps = 4
signal, bkps = rpt.pw_constant(n_samples, n_dims, n_bkps, noise_std=sigma)
figs, axs = [], []
for i in range(signal.shape[1]):
points = signal[:,i]
# detection of change points
algo = rpt.Dynp(model='l2').fit(points)
result = algo.predict(n_bkps=2)
fig, ax = rpt.display(points, bkps, result, figsize=(15,3))
figs.append(fig)
axs.append(ax)
plt.show()
I had a look at the source code of ruptures.display(), and it accepts **kwargs that are passed on to matplotlib. This allows us to redirect the output to a single figure, and with gridspec, we can position individual subplots within this figure:
import ruptures as rpt
import matplotlib.pyplot as plt
n_samples, n_dims, sigma = 100, 9, 2
n_bkps = 4
signal, bkps = rpt.pw_constant(n_samples, n_dims, n_bkps, noise_std=sigma)
#number of subplots
n_subpl = signal.shape[1]
#give figure a name to refer to it later
fig = plt.figure(num = "ruptures_figure", figsize=(8, 15))
#define grid of nrows x ncols
gs = fig.add_gridspec(n_subpl, 1)
for i in range(n_subpl):
points = signal[:,i]
algo = rpt.Dynp(model='l2').fit(points)
result = algo.predict(n_bkps=2)
#rpt.display(points, bkps, result)
#plot into predefined figure
_, curr_ax = rpt.display(points, bkps, result, num="ruptures_figure")
#position current subplot within grid
curr_ax[0].set_position(gs[i].get_position(fig))
curr_ax[0].set_subplotspec(gs[i])
plt.show()
Sample output:
I am trying to rebuild an image that I previously decomposed with SVD. The image is this:
I successfully decomposed the image with this code:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
img = Image.open('steve.jpg')
img = np.mean(img, 2)
U,s,V = np.linalg.svd(img)
s an array of the singular values of the image. The more singular values I take, the more the reconstructed image is similar to the original one.
For example, if I take 20 singular values:
n = 20
S = np.zeros(np.shape(img))
for i in range(0, n):
S[i, i] = s[i]
recon_img = U#S#V
plt.imshow(recon_img)
plt.axis('off')
plt.show()
I would like to fix the minumum number of singular values in order to get a good result: an image pretty similary to the original one. Moreover, I would like to see how much the result changes when I take a higher number of singular values. I tried with an animation without success:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
img = Image.open('steve.jpg')
img = np.mean(img, 2)
U,s,V = np.linalg.svd(img)
fig = plt.figure()
def update(i):
S = np.zeros(np.shape(img))
n = 20
for i in range(0, n):
S[i, i] = s[i]
recon_img = U#S#V
plt.imshow(recon_img)
plt.axis('off')
ani = FuncAnimation(fig = fig, func = update, frames = 20, interval = 10)
plt.show()
If you plot the s singular values you can see a very steep decreasing curve, better if you use a log scale for the y axis:
plt.semilogy(s, 'k-')
As you can see, the first 50 singular values are the most important ones: almost everyone more that 1000. Values from the ~50th to the ~250th are an order of magnitude lower and their values decreases slowly: the slope of the curve is contained (remember the logarithmic y scale). That beeing said I would take the first 50 elements to rebulid your image.
Regarding the animation:
while the animation updates frame by frame, the counter i is increased by 1. In your code, you mistakenly use i to slice the s and define S; you should rename the counter.
Moreover, as animation goes on, you need to take an increasing number of singular values, this is set by n which you keep constant frame by frame. You need to update n at each loop, so you can use it as the counter.
Furthermore, you need the erase the previous plotted image, so you need to add a plt.gca().cla() at the beginning of the update function.
Check the code below for reference:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
img = Image.open('steve.jpg')
img = np.mean(img, 2)
U,s,V = np.linalg.svd(img)
fig, ax = plt.subplots(1, 2, figsize = (4, 4))
ax[0].imshow(img)
ax[0].axis('off')
ax[0].set_title('Original')
def init():
ax[1].cla()
ax[1].imshow(np.zeros(np.shape(img)))
ax[1].axis('off')
ax[1].set_title('Reconstructed\nn = 00')
def update(n):
ax[1].cla()
S = np.zeros(np.shape(img))
for i in range(0, n):
S[i, i] = s[i]
recon_img = U#S#V
ax[1].imshow(recon_img)
ax[1].axis('off')
ax[1].set_title(f'Reconstructed\nn = {n:02}')
ani = FuncAnimation(fig = fig, func = update, frames = 50, init_func = init, interval = 10)
ani.save('ani.gif', writer = 'imagemagick')
plt.show()
which gives this animation:
As you can see, the first 50 elements are enough to rebuild you image pretty well. The rest of the elements adds some noise and changes a little the background.
I'm trying to make an animated 3-D scatter plot with the ability to plot a dynamic number of classes as different colors. This is one of the attempts. I've included the whole code in case it is helpful, and marked the trouble spot with a row of stars:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation
from random import uniform
x_arr,y_arr,depth_arr,time_arr,ml_arr,cluster_arr = np.loadtxt(data, unpack=5, usecols=(0, 1, 2, 5, 6))
class Point:
def __init__(self,x,y,depth,time,cluster):
self.x=x
self.y=y
self.depth=depth
self.time=time
self.cluster=cluster
points = []
for i in range(0,len(x_arr)):
points.append(Point(x_arr[i],y_arr[i],depth_arr[i],time_arr[i],cluster_arr[i]))
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_xlim(min(x_arr), max(x_arr))
ax.set_ylim(min(y_arr), max(y_arr))
ax.set_zlim(min(depth_arr), max(depth_arr))
colors_1 = plt.cm.jet(np.linspace(0,max(cluster_arr),max(cluster_arr)+1))
colors = colors_1.reshape(-1,4)
def plot_points(time):
x = []
y = []
z = []
clust = []
points_cp = list(np.copy(points))
for i in range(0,(int(max(cluster_arr))+1)):
for event in points_cp:
if event.cluster == i:
if event.time < time:
points_cp.remove(event)
elif event.time <= time + 86400:
x.append(event.x)
y.append(event.y)
z.append(event.depth)
clust.append(event.cluster)
points_cp.remove(event)
# **************************************************************
color_ind = 0
first_ind = 0
last_ind = 0
for i in range(0,len(x)):
if clust[i] != color_ind:
last_ind = i
for i in range(0,len(x)):
ax.scatter(x[first_ind:last_ind],y[first_ind:last_ind],z[first_ind:last_ind],c=colors[int(color_ind)])
color_ind = clust[i]
first_ind = i
time = np.linspace(min(time_arr),max(time_arr),100)
ani = animation.FuncAnimation(fig,plot_points,time)
plt.show()
This gives me a plot with the correct colors, but once a point is plotted, it remains throughout the entire animation.
I have also tried set_x, set_color, etc., but this doesn't work with a loop (it is updated with each iteration, so that only the last class is actually plotted), and I need to use a for loop to accommodate a variable number of classes. I've tried using a colormap with a fixed extent, but have been unsuccessful, as colormapping doesn't work with the plot function, and I haven't been able to get the rest of the code to work with a scatter function.
Thanks in advance for your help, and my apologies if the code is a little wonky. I'm pretty new to this.
I am trying to make an animation with matplotlib.animation, using the ArtistAnimation function. I have a large matrix and I want to show a different part of this matrix in a colormap using plt.imshow() in each frame. However, only one frame is shown in the animation and I don't know why. I tried to mimic the code in An animated image using a list of images. I have looked at this question and this question, but I could not find a solution there.
This is my code:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
def make_animation(pwv_matrix, length_side):
pwv_shape = pwv_matrix.shape
fig = plt.figure()
num_frames = min(pwv_shape[0], pwv_shape[1])-length_side
y_min = int(np.round(pwv_shape[0]/2) - np.round(length_side/2))
y_max = int(np.round(pwv_shape[0]/2) + np.round(length_side/2))
x_min = 0
x_max = length_side
ims = []
for i in range(0, num_frames):
pwv_frame = pwv_matrix[x_min:x_max, y_min:y_max]
im = plt.imshow(pwv_frame, animated=True, cmap = 'viridis')
ims.append([im])
x_min += 1
x_max += 1
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True,
repeat_delay=1000)
plt.show()
x = np.linspace(0, 1, 200)
y = np.linspace(0, 1, 200)
pwv_matrix, yv = np.meshgrid(x, y)
length_side = 20 #m
make_animation(pwv_matrix, length_side)
where self.pwv_matrix is a large matrix obtained from a .dat file. Does anyone see the problem?
Thanks very much in advance!
Try adding a very small delay after the show() function with
matplotlib.pyplot.pause(interval)[source]
function or:
from time import sleep
sleep(0.05)
The actual drawing of the image occurs one you give it some processing time (in a very simplified way of explaining it)
If it does not work there are some other things you might try, let me know if it doesn't work
So I've successfully plotted pie charts on a map as markers using ax.scatter, but I'm having trouble with some of the wedges "exploding" out of the pie chart. I can't seem to find the reason for this in my code, and have been unable to find an explanation anywhere online. This code is based on the example here , which a colleague has also used and resulted in normal, uniform pie charts. Between us we can't find the issue, and no errors occur.
The code:
import numpy as np
import math
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap, cm
australia_data = np.zeros((24,12))
colors = ['red','yellow','blue','mediumorchid']
#pie chart locations
xlon=[146.7,166,101.6,137.4,145.1,113.6,169.7,113.3,176.0,139.6,148.9,124.2,132.4,142.0,129.6,148.0,116.5,142.8,141.7,128.0,113.6,120.7,128.3,148.6]
ylat=[-42.2,-19.2,-0.5,-3.5,-34.4,-8.7,-45.1,-1.0,-38.6,-26.7,-29.1,-20.0,-14.4,-18.9,-31.3,-6.6,-23.8,-3.4,-7.5,-25.6,3.8,-3.1,-1.9,-23.2]
#function to draw pie charts on map
def draw_pie(ax,X=0, Y=0, size = 1500):
xy = []
start = 0.17
ratios=[1/12.]*12
for ratio in ratios:
x = [0] + np.cos(np.linspace(2*math.pi*start,2*math.pi*(start+ratio))).tolist() #30
y = [0] + np.sin(np.linspace(2*math.pi*start,2*math.pi*(start+ratio))).tolist() #30
xy1=(zip(x,y))
xy.append(xy1)
start -= ratio
piecolors = []
for lt in range(12):
c = australia_data[b,lt]-1
c=int(c)
piecolors.append(colors[c])
for i, xyi in enumerate(xy):
ax.scatter([X],[Y] , marker=(xyi,0), s=size, facecolor=piecolors[i],linewidth=0.5,alpha=.7)
australia_data[:,11] = 1
australia_data[:,4] = 3
australia_data[:,1] = 2
fig = plt.figure()
ax = fig.add_axes([.05,.01,.79,.95])
x1 = 90 #left
x2 = 180 #right
y1 = -50 #bottom
y2 = 10 #top
#Create the map
m = Basemap(resolution='l',projection='merc', llcrnrlat=y1,urcrnrlat=y2,llcrnrlon=x1,urcrnrlon=x2,lat_ts=0) #,lat_ts=(x1+x2)/2
m.drawcoastlines()
#plots pie charts:
for b in range(24):
X,Y=m(xlon[b],ylat[b])
draw_pie(ax,X, Y,size=400)
plt.savefig('australia_pies.png',dpi=400)
Any ideas as to why this is happening (and how to fix it!) would be greatly appreciated!
Edit: it seems to be an issue with the number of wedges in the pie chart - reducing this to 6 results in uniform pies, but 7+ causes some wedges to "explode".
Looking at the scatter piecharts example, you forgot to adjust the size of the pie wedges according to the maximum distance from 0 to the the arc of the wedge. This is necessary because markers normalize the path given before drawing it, hence different wedges need different sizes in order to appear with the same size in the final plot.
import numpy as np
import matplotlib.pyplot as plt
#function to draw pie charts on map
def draw_pie(ax,X=0, Y=0, size = 1500):
xy = []; s=[]
start = 0.0
ratios=[1/12.]*12
for ratio in ratios:
x = [0] + np.cos(np.linspace(2*np.pi*start,2*np.pi*(start+ratio))).tolist() #30
y = [0] + np.sin(np.linspace(2*np.pi*start,2*np.pi*(start+ratio))).tolist() #30
xy1 = np.column_stack([x, y])
s1 = np.abs(xy1).max()
xy.append(xy1)
s.append(s1)
start -= ratio
for xyi, si in zip(xy,s):
ax.scatter([X],[Y] , marker=(xyi,0), s=size*si**2, edgecolor="k")
fig, ax = plt.subplots()
X,Y=166,50
draw_pie(ax,X, Y,size=3000)
plt.show()