I am new using python and I would like to ask you a problem that I have with my current code. I am solving a partial differential equation (1D in space) and I want to make an animation at each time for the given numerical solution, but I don't want to save all the arrays of the solution at each time (because this is not efficient)
For simplicity I just show you the analytical solution of the differential equation.
I have tried to make a plot for each time steep trying to make an animation, but as I have read in other places it's not too much efficient due to the plt.pause()
import numpy as np
import math
from matplotlib import pyplot as plt
pi = math.pi
xmin = 0.0
xmax = 10.0
N = 100 #number of points
x = np.arange(xmin, xmax , (xmax-xmin)/N)
def solution(t):
p = np.exp(-x**2/(4*k*t))/(np.sqrt(4.*pi*k*t))
return p
t_final = 10.0
t_initial = 0.0
t = t_initial
dt = 0.1
k = 1.0
while t<t_final:
t +=dt
pp = solution(t)
plt.ion()
plt.xlabel('x')
plt.ylabel('P')
plt.plot(x, pp, 'r-',label = "t=%s" % t)
plt.legend(loc='upper right')
plt.draw()
plt.pause(10**(-20))
plt.show()
plt.clf()
Do you know how can be re-implemented my code to make an animation (and save it) without save the data ?
Here is how to use FuncAnimation to generate the desired animation
import numpy as np
import math
from matplotlib import pyplot as plt
pi = math.pi
xmin = 0.0
xmax = 10.0
N = 100 #number of points
x = np.arange(xmin, xmax , (xmax-xmin)/N)
t_initial = 0.0
t_final = 10.0
dt = 0.1
k = 1.0
fig, ax = plt.subplots()
ax.set_xlabel('x')
ax.set_ylabel('P')
plotLine, = ax.plot(x, np.zeros(len(x))*np.NaN, 'r-')
plotTitle = ax.set_title("t=0")
ax.set_ylim(0,1.)
ax.set_xlim(xmin,xmax)
def solution(t):
p = np.exp(-x**2/(4*k*t))/(np.sqrt(4.*pi*k*t))
return p
def animate(t):
pp = solution(t)
plotLine.set_ydata(pp)
plotTitle.set_text(f"t = {t:.1f}")
#ax.relim() # use if autoscale desired
#ax.autoscale()
return [plotLine,plotTitle]
ani = animation.FuncAnimation(fig, func=animate, frames=np.arange(t_initial, t_final+dt, dt), blit=True)
plt.show()
This way uses a library I wrote, celluloid. With it I only had to change a handful of lines from your code: mainly the few calls with celluloid and changing the legend creation slightly.
import numpy as np
import math
from matplotlib import pyplot as plt
from celluloid import Camera
pi = math.pi
xmin = 0.0
xmax = 10.0
N = 100 #number of points
x = np.arange(xmin, xmax , (xmax-xmin)/N)
def solution(t):
p = np.exp(-x**2/(4*k*t))/(np.sqrt(4.*pi*k*t))
return p
t_final = 10.0
t_initial = 0.0
t = t_initial
dt = 0.1
k = 1.0
fig = plt.figure()
camera = Camera(fig)
plt.xlabel('x')
plt.ylabel('P')
while t<t_final:
t +=dt
pp = solution(t)
line = plt.plot(x, pp, 'r-')
plt.legend(line, ['t={:.1f}'.format(t)], loc='upper right')
camera.snap()
animation = camera.animate()
animation.save('animation.mp4')
Related
I am trying to create an animation of a Monte-Carlo estimation of the number pi, for each iteration I would like the numerical estimation to be in text on the plot, but the previous text is not removed and makes the values unreadable. I tried Artist.remove(frame) with no success. The plot is done with Jupiter Notebook.
#Enable interactive plot
%matplotlib notebook
import math
from matplotlib.path import Path
from matplotlib.animation import FuncAnimation
from matplotlib.path import Path
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import ConvexHull
from matplotlib.artist import Artist
N = 10000
#create necessary arrays
x = np.arange(0,N)
y = np.zeros(N)
#set initial points to zero
inHull = 0
def inCircle(point):
#the function is given a point in R^n
#returns a boolean stating if the norm of the point is smaller than 1.
if np.sum(np.square(point)) <= 1:
return True
else:
return False
#iterate over each point
for i in range(N):
random_point = np.random.rand(2)*2 - 1
#determine if the point is inside the hull
if inCircle(random_point):
inHull += 1
#we store areas in array y.
y[i] = (inHull*4)/(i+1)
fig = plt.figure()
ax = plt.subplot(1, 1, 1)
data_skip = 20
def init_func():
ax.clear()
plt.xlabel('n points')
plt.ylabel('Estimated area')
plt.xlim((x[0], x[-1]))
plt.ylim((min(y)- 1, max(y)+0.5))
def update_plot(i):
ax.plot(x[i:i+data_skip], y[i:i+data_skip], color='k')
ax.scatter(x[i], y[i], color='none')
Artist.remove(ax.text(N*0.6, max(y)+0.25, "Estimation: "+ str(round(y[i],5))))
ax.text(N*0.6, max(y)+0.25, "Estimation: "+ str(round(y[i],5)))
anim = FuncAnimation(fig,
update_plot,
frames=np.arange(0, len(x), data_skip),
init_func=init_func,
interval=20)
plt.show()
Thank you.
As you have already done in init_func, you should clear the plot in each iteration with ax.clear(). Then it is necessary to edit slighlty the plot function:
ax.plot(x[i:i+data_skip], y[i:i+data_skip], color='k')
And finally you have to fix x axis limits in each iteration with ax.set_xlim(0, N).
Complete Code
#Enable interactive plot
%matplotlib notebook
import math
from matplotlib.path import Path
from matplotlib.animation import FuncAnimation
from matplotlib.path import Path
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import ConvexHull
from matplotlib.artist import Artist
N = 10000
# create necessary arrays
x = np.arange(0, N)
y = np.zeros(N)
# set initial points to zero
inHull = 0
def inCircle(point):
# the function is given a point in R^n
# returns a boolean stating if the norm of the point is smaller than 1.
if np.sum(np.square(point)) <= 1:
return True
else:
return False
# iterate over each point
for i in range(N):
random_point = np.random.rand(2)*2 - 1
# determine if the point is inside the hull
if inCircle(random_point):
inHull += 1
# we store areas in array y.
y[i] = (inHull*4)/(i + 1)
fig = plt.figure()
ax = plt.subplot(1, 1, 1)
data_skip = 20
txt = ax.text(N*0.6, max(y) + 0.25, "")
def init_func():
ax.clear()
plt.xlabel('n points')
plt.ylabel('Estimated area')
plt.xlim((x[0], x[-1]))
plt.ylim((min(y) - 1, max(y) + 0.5))
def update_plot(i):
ax.clear()
ax.plot(x[:i + data_skip], y[:i + data_skip], color = 'k')
ax.scatter(x[i], y[i], color = 'none')
ax.text(N*0.6, max(y) + 0.25, "Estimation: " + str(round(y[i], 5)))
ax.set_xlim(0, N)
anim = FuncAnimation(fig,
update_plot,
frames = np.arange(0, len(x), data_skip),
init_func = init_func,
interval = 20)
plt.show()
Animation
I can evaluate the value of pi using different data points by Python. But for each repeat I want to plot the scatter plot like this:
My python code for finding pi using monte carlo method is :
from random import *
from math import sqrt
inside=0
n=10**6
for i in range(0,n):
x=random()
y=random()
if sqrt(x*x+y*y)<=1:
inside+=1
pi=4*inside/n
print (pi)
If you get errors about the backend use this:
import matplotlib as mp
mp.use('Tkagg')
Which will set the backend to TkAgg, which uses the Tkinter user interface toolkit.
import numpy as np
import matplotlib.pyplot as plt
n=1e3
x = 1-2*np.random.random(int(n))
y = 1-2.*np.random.random(int(n))
insideX, insideY = x[(x*x+y*y)<=1],y[(x*x+y*y)<=1]
outsideX, outsideY = x[(x*x+y*y)>1],y[(x*x+y*y)>1]
fig, ax = plt.subplots(1)
ax.scatter(insideX, insideY, c='b', alpha=0.8, edgecolor=None)
ax.scatter(outsideX, outsideY, c='r', alpha=0.8, edgecolor=None)
ax.set_aspect('equal')
fig.show()
To further elaborate Robbie's code:
import numpy as np
import matplotlib.pyplot as plt
n = 1000
xy = np.random.uniform(-1, 1, 2 * n).reshape((2, n))
in_marker = xy[0]**2 + xy[1]**2 <= 1
pi = np.sum(in_marker) / n * 4
in_xy = xy[:, in_marker]
out_xy = xy[:, ~in_marker]
fig, ax = plt.subplots(1)
ax.scatter(*in_xy,c='b')
ax.scatter(*out_xy,c='r')
ax.set_aspect('equal')
fig.show()
building from your code, this may get you started:
import matplotlib.pyplot as plt
from random import random
inside = 0
n = 10**3
x_inside = []
y_inside = []
x_outside = []
y_outside = []
for _ in range(n):
x = random()
y = random()
if x**2+y**2 <= 1:
inside += 1
x_inside.append(x)
y_inside.append(y)
else:
x_outside.append(x)
y_outside.append(y)
pi = 4*inside/n
print(pi)
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.scatter(x_inside, y_inside, color='g', marker='s')
ax.scatter(x_outside, y_outside, color='r', marker='s')
fig.show()
although i prefer this answer that uses numpy from the start...
Here is a variation on hiro protagonist's code, using random.uniform() to allow for random numbers between -1.0 and 1.0, allowing all the points to be plotted, and not just 1/4 of it (not the most elegant code, but it is spelled-out to learn the basics of the Monte Carlo Simulation):
import matplotlib.pyplot as plt
import random
inside = 0
n = 10**3
x_inside = []
y_inside = []
x_outside = []
y_outside = []
for _ in range(n):
x = random.uniform(-1.0,1.0)
y = random.uniform(-1.0,1.0)
if x**2+y**2 <= 1:
inside += 1
x_inside.append(x)
y_inside.append(y)
else:
x_outside.append(x)
y_outside.append(y)
To estimate pi, the points in the circle correspond to the area of the circle enclosing it (pi*radius^2) and the total points correspond to the area of the square enclosing it (2*radius)^2. So this translates into:
(points in the circle)/(total points) = (pi*radius^2)/(2*radius)^2
Solving for pi, the equation becomes:
pi=4*(points in the circle)/(total points)
pi = 4*inside/n
print(pi)
Plot the points inside and outside the circle:
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.scatter(x_inside, y_inside, color='g', marker='s')
ax.scatter(x_outside, y_outside, color='r', marker='s')
fig.show()
Plot of points inside and outside the circle
I can evaluate the value of pi using different data points by Python. But for each repeat I want to plot the scatter plot like this:
My python code for finding pi using monte carlo method is :
from random import *
from math import sqrt
inside=0
n=10**6
for i in range(0,n):
x=random()
y=random()
if sqrt(x*x+y*y)<=1:
inside+=1
pi=4*inside/n
print (pi)
If you get errors about the backend use this:
import matplotlib as mp
mp.use('Tkagg')
Which will set the backend to TkAgg, which uses the Tkinter user interface toolkit.
import numpy as np
import matplotlib.pyplot as plt
n=1e3
x = 1-2*np.random.random(int(n))
y = 1-2.*np.random.random(int(n))
insideX, insideY = x[(x*x+y*y)<=1],y[(x*x+y*y)<=1]
outsideX, outsideY = x[(x*x+y*y)>1],y[(x*x+y*y)>1]
fig, ax = plt.subplots(1)
ax.scatter(insideX, insideY, c='b', alpha=0.8, edgecolor=None)
ax.scatter(outsideX, outsideY, c='r', alpha=0.8, edgecolor=None)
ax.set_aspect('equal')
fig.show()
To further elaborate Robbie's code:
import numpy as np
import matplotlib.pyplot as plt
n = 1000
xy = np.random.uniform(-1, 1, 2 * n).reshape((2, n))
in_marker = xy[0]**2 + xy[1]**2 <= 1
pi = np.sum(in_marker) / n * 4
in_xy = xy[:, in_marker]
out_xy = xy[:, ~in_marker]
fig, ax = plt.subplots(1)
ax.scatter(*in_xy,c='b')
ax.scatter(*out_xy,c='r')
ax.set_aspect('equal')
fig.show()
building from your code, this may get you started:
import matplotlib.pyplot as plt
from random import random
inside = 0
n = 10**3
x_inside = []
y_inside = []
x_outside = []
y_outside = []
for _ in range(n):
x = random()
y = random()
if x**2+y**2 <= 1:
inside += 1
x_inside.append(x)
y_inside.append(y)
else:
x_outside.append(x)
y_outside.append(y)
pi = 4*inside/n
print(pi)
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.scatter(x_inside, y_inside, color='g', marker='s')
ax.scatter(x_outside, y_outside, color='r', marker='s')
fig.show()
although i prefer this answer that uses numpy from the start...
Here is a variation on hiro protagonist's code, using random.uniform() to allow for random numbers between -1.0 and 1.0, allowing all the points to be plotted, and not just 1/4 of it (not the most elegant code, but it is spelled-out to learn the basics of the Monte Carlo Simulation):
import matplotlib.pyplot as plt
import random
inside = 0
n = 10**3
x_inside = []
y_inside = []
x_outside = []
y_outside = []
for _ in range(n):
x = random.uniform(-1.0,1.0)
y = random.uniform(-1.0,1.0)
if x**2+y**2 <= 1:
inside += 1
x_inside.append(x)
y_inside.append(y)
else:
x_outside.append(x)
y_outside.append(y)
To estimate pi, the points in the circle correspond to the area of the circle enclosing it (pi*radius^2) and the total points correspond to the area of the square enclosing it (2*radius)^2. So this translates into:
(points in the circle)/(total points) = (pi*radius^2)/(2*radius)^2
Solving for pi, the equation becomes:
pi=4*(points in the circle)/(total points)
pi = 4*inside/n
print(pi)
Plot the points inside and outside the circle:
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.scatter(x_inside, y_inside, color='g', marker='s')
ax.scatter(x_outside, y_outside, color='r', marker='s')
fig.show()
Plot of points inside and outside the circle
I am trying to plot a function with two parameters. In this case I'd like the function to plot with respect to "yy" in order for it to be in polar coordinates. When I run the program I get 10 figures rather than 1 single plot. Is there a reason this happens? Also, I'm not getting a plot at all.
import scipy.optimize as opt
import matplotlib.pyplot as plt
import pylab as pyl
freq = 9.75e9
lmda = 299792458./freq
k = 2*np.pi/lmda
h1 = 0.25*lmda
def theta(x,y):
th = np.arctan(y,x)
return th
def F(x,y):
f=2*np.abs(np.sin(k*h1*theta(x,y)))
return f
def gain(x,y):
return 10*np.log10(F(x,y)**2)
xx = np.arange(0,2000,200)
yy = np.linspace(0,np.pi/2,1000)
for tval in xx:
plt.rcParams['text.latex.preamble']=[r'\usepackage{amsmath}']
plt.rc('text',usetex=True)
font = {'family':'serif','size':20}
plt.rc('font',**font)
fig, ax=plt.subplots(subplot_kw=dict(projection='polar'))
ticks = np.arange(0,360,45)
ax.set_ylim(-40,10)
ax.set_yticks([-40,-30,-20,-10,0])
ax.set_yticklabels(['','30','20','10',''],verticalalignment='center',horizontalalignment='center')
ax.set_thetagrids(ticks, frac=1.2)
ax.set_xlim(0, np.pi/2)
ax.set_theta_zero_location('N') # changes the orienation of theta
ax.plot(yy,gain(yy,tval)) #dipole elevation plane pattern
plt.tight_layout()
plt.show()
You need a little restructure:
import scipy.optimize as opt
import matplotlib.pyplot as plt
import pylab as pyl
import numpy as np
freq = 9.75e9
lmda = 299792458./freq
k = 2*np.pi/lmda
h1 = 0.25*lmda
def theta(x,y):
th = np.arctan(y,x)
return th
def F(x,y):
f=2*np.abs(np.sin(k*h1*theta(x,y)))
return f
def gain(x,y):
return 10*np.log10(F(x,y)**2)
xx = np.arange(0,2000,200)
yy = np.linspace(0,np.pi/2,1000)
plt.rcParams['text.latex.preamble']=[r'\usepackage{amsmath}']
plt.rc('text',usetex=True)
font = {'family':'serif','size':20}
plt.rc('font',**font)
fig, ax=plt.subplots(subplot_kw=dict(projection='polar'))
ticks = np.arange(0,360,45)
ax.set_ylim(-40,10)
ax.set_yticks([-40,-30,-20,-10,0])
ax.set_yticklabels(['','30','20','10',''],verticalalignment='center',horizontalalignment='center')
ax.set_thetagrids(ticks, frac=1.2)
ax.set_xlim(0, np.pi/2)
ax.set_theta_zero_location('N') # changes the orienation of theta
for tval in xx:
ax.plot(yy,gain(yy,tval)) #dipole elevation plane pattern
plt.tight_layout()
plt.show()
You also should handle the division by zero error.
Given a mean and a variance is there a simple function call which will plot a normal distribution?
import matplotlib.pyplot as plt
import numpy as np
import scipy.stats as stats
import math
mu = 0
variance = 1
sigma = math.sqrt(variance)
x = np.linspace(mu - 3*sigma, mu + 3*sigma, 100)
plt.plot(x, stats.norm.pdf(x, mu, sigma))
plt.show()
I don't think there is a function that does all that in a single call. However you can find the Gaussian probability density function in scipy.stats.
So the simplest way I could come up with is:
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
# Plot between -10 and 10 with .001 steps.
x_axis = np.arange(-10, 10, 0.001)
# Mean = 0, SD = 2.
plt.plot(x_axis, norm.pdf(x_axis,0,2))
plt.show()
Sources:
http://www.johndcook.com/distributions_scipy.html
http://docs.scipy.org/doc/scipy/reference/stats.html
http://telliott99.blogspot.com/2010/02/plotting-normal-distribution-with.html
Use seaborn instead
i am using distplot of seaborn with mean=5 std=3 of 1000 values
value = np.random.normal(loc=5,scale=3,size=1000)
sns.distplot(value)
You will get a normal distribution curve
If you prefer to use a step by step approach you could consider a solution like follows
import numpy as np
import matplotlib.pyplot as plt
mean = 0; std = 1; variance = np.square(std)
x = np.arange(-5,5,.01)
f = np.exp(-np.square(x-mean)/2*variance)/(np.sqrt(2*np.pi*variance))
plt.plot(x,f)
plt.ylabel('gaussian distribution')
plt.show()
Unutbu answer is correct.
But because our mean can be more or less than zero I would still like to change this :
x = np.linspace(-3 * sigma, 3 * sigma, 100)
to this :
x = np.linspace(-3 * sigma + mean, 3 * sigma + mean, 100)
I believe that is important to set the height, so created this function:
def my_gauss(x, sigma=1, h=1, mid=0):
from math import exp, pow
variance = pow(sigma, 2)
return h * exp(-pow(x-mid, 2)/(2*variance))
Where sigma is the standard deviation, h is the height and mid is the mean.
To:
plt.close("all")
x = np.linspace(-20, 20, 101)
yg = [my_gauss(xi) for xi in x]
Here is the result using different heights and deviations:
I have just come back to this and I had to install scipy as matplotlib.mlab gave me the error message MatplotlibDeprecationWarning: scipy.stats.norm.pdf when trying example above. So the sample is now:
%matplotlib inline
import math
import matplotlib.pyplot as plt
import numpy as np
import scipy.stats
mu = 0
variance = 1
sigma = math.sqrt(variance)
x = np.linspace(mu - 3*sigma, mu + 3*sigma, 100)
plt.plot(x, scipy.stats.norm.pdf(x, mu, sigma))
plt.show()
you can get cdf easily. so pdf via cdf
import numpy as np
import matplotlib.pyplot as plt
import scipy.interpolate
import scipy.stats
def setGridLine(ax):
#http://jonathansoma.com/lede/data-studio/matplotlib/adding-grid-lines-to-a-matplotlib-chart/
ax.set_axisbelow(True)
ax.minorticks_on()
ax.grid(which='major', linestyle='-', linewidth=0.5, color='grey')
ax.grid(which='minor', linestyle=':', linewidth=0.5, color='#a6a6a6')
ax.tick_params(which='both', # Options for both major and minor ticks
top=False, # turn off top ticks
left=False, # turn off left ticks
right=False, # turn off right ticks
bottom=False) # turn off bottom ticks
data1 = np.random.normal(0,1,1000000)
x=np.sort(data1)
y=np.arange(x.shape[0])/(x.shape[0]+1)
f2 = scipy.interpolate.interp1d(x, y,kind='linear')
x2 = np.linspace(x[0],x[-1],1001)
y2 = f2(x2)
y2b = np.diff(y2)/np.diff(x2)
x2b=(x2[1:]+x2[:-1])/2.
f3 = scipy.interpolate.interp1d(x, y,kind='cubic')
x3 = np.linspace(x[0],x[-1],1001)
y3 = f3(x3)
y3b = np.diff(y3)/np.diff(x3)
x3b=(x3[1:]+x3[:-1])/2.
bins=np.arange(-4,4,0.1)
bins_centers=0.5*(bins[1:]+bins[:-1])
cdf = scipy.stats.norm.cdf(bins_centers)
pdf = scipy.stats.norm.pdf(bins_centers)
plt.rcParams["font.size"] = 18
fig, ax = plt.subplots(3,1,figsize=(10,16))
ax[0].set_title("cdf")
ax[0].plot(x,y,label="data")
ax[0].plot(x2,y2,label="linear")
ax[0].plot(x3,y3,label="cubic")
ax[0].plot(bins_centers,cdf,label="ans")
ax[1].set_title("pdf:linear")
ax[1].plot(x2b,y2b,label="linear")
ax[1].plot(bins_centers,pdf,label="ans")
ax[2].set_title("pdf:cubic")
ax[2].plot(x3b,y3b,label="cubic")
ax[2].plot(bins_centers,pdf,label="ans")
for idx in range(3):
ax[idx].legend()
setGridLine(ax[idx])
plt.show()
plt.clf()
plt.close()
import math
import matplotlib.pyplot as plt
import numpy
import pandas as pd
def normal_pdf(x, mu=0, sigma=1):
sqrt_two_pi = math.sqrt(math.pi * 2)
return math.exp(-(x - mu) ** 2 / 2 / sigma ** 2) / (sqrt_two_pi * sigma)
df = pd.DataFrame({'x1': numpy.arange(-10, 10, 0.1), 'y1': map(normal_pdf, numpy.arange(-10, 10, 0.1))})
plt.plot('x1', 'y1', data=df, marker='o', markerfacecolor='blue', markersize=5, color='skyblue', linewidth=1)
plt.show()
For me, this worked pretty well if you are trying to plot a particular pdf
theta1 = {
"a": 0.5,
"cov" : 1,
"mean" : 0
}
x = np.linspace(start = 0, stop = 1000, num = 1000)
pdf = stats.norm.pdf(x, theta1['mean'], theta1['cov']) + theta2['a']
sns.lineplot(x,pdf)