I am trying to plot a 3D surface but I am having some trouble because the documentation for matplotlib does not appear to be very thorough and is lacking in examples. Anyways the program I have written is to solve the Heat Equation Numerically via Method of Finite Differences. Here is my code:
## This program is to implement a Finite Difference method approximation
## to solve the Heat Equation, u_t = k * u_xx,
## in 1D w/out sources & on a finite interval 0 < x < L. The PDE
## is subject to B.C: u(0,t) = u(L,t) = 0,
## and the I.C: u(x,0) = f(x).
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
# Parameters
L = 1 # length of the rod
T = 10 # terminal time
N = 40 # spatial values
M = 1600 # time values/hops; (M ~ N^2)
s = 0.25 # s := k * ( (dt) / (dx)^2 )
# uniform mesh
x_init = 0
x_end = L
dx = float(x_end - x_init) / N
x = np.arange(x_init, x_end, dx)
x[0] = x_init
# time discretization
t_init = 0
t_end = T
dt = float(t_end - t_init) / M
t = np.arange(t_init, t_end, dt)
t[0] = t_init
# time-vector
for m in xrange(0, M):
t[m] = m * dt
# spatial-vector
for j in xrange(0, N):
x[j] = j * dx
# definition of the solution u(x,t) to u_t = k * u_xx
u = np.zeros((N, M+1)) # array to store values of the solution
# Finite Difference Scheme:
u[:,0] = x * (x - 1) #initial condition
for m in xrange(0, M):
for j in xrange(1, N-1):
if j == 1:
u[j-1,m] = 0 # Boundary condition
elif j == N-1:
u[j+1,m] = 0 # Boundary Condition
else:
u[j,m+1] = u[j,m] + s * ( u[j+1,m] -
2 * u[j,m] + u[j-1,m] )
This is what I have written to try and plot a 3D surface graph:
# for 3D graph
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
surf = ax.plot_surface(x, t, u, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
I am getting this error when I run the code to plot the graph: "ValueError: shape mismatch: two or more arrays have incompatible dimensions on axis 1."
Please, any and all help is very greatly appreicated. I think the error comes up because I defined u to be a Nx(M+1) matrix but it is necessary to make the original program run. I am unsure of how to correct this so the graph plots properly. Thanks!
Use this code (look at the comments):
# plot 3d surface
# create a meshgrid of (x,t) points
# T and X are 2-d arrays
T, X = np.meshgrid(t,x)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# Use X and T arrays to plot u
# shape of X, T and u must to be the same
# but shape of u is [40,1601] and I will skip last row while plotting
surf = ax.plot_surface(X, T, u[:,:1600], rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
Result:
because the documentation for matplotlib does not appear to be very thorough and is lacking in examples
http://matplotlib.org/examples/mplot3d/index.html
It's helpful to print out the shapes of the variables x, t, and u:
x.shape == (40,)
t.shape == (1600,)
u.shape == (40, 1601)
So there are two problems here.
The first one is that x and t are 1-dimensional, even though they need to be 2-dimensional.
And the second one is that u has one more element than t in the second dimension.
You can fix both by running
t, x = np.meshgrid(t, x)
u = u[:,:-1]
before creating the 3d plot.
Related
Given:
and
We have formula:
I make 3D model, but I can't give the condition like when x = 0 u(0,t) = 0
import math
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
def u(x,t,n):
for i in range(1,n):
alpha=((6*(-1)**i-30)/(i**2*np.pi**2))
e=np.exp((-(np.pi**2)*(i**2)*t))
sin=np.sin((i*np.pi*x)/3)
u=alpha*e*sin
return u
N=20
L = 4 # length
att = 20 # iteration
x = np.linspace(0, L ,N) #x-array
t = np.linspace(0, L, N) #t-array
X, Y = np.meshgrid(x, t)
Z = u(X, Y, att)
fig = plt.figure(figsize = (10,10))
ax = fig.add_subplot(111, projection='3d')
ax.plot_wireframe(X, Y, Z, rstride=10, cstride=1000)
plt.show()
My 3D model:
It would help if you actually computed a sum in the partial Fourier sum calculation, at the moment you just return the last term of that sum.
def u(x,t,n):
u = 0*x
for i in range(1,n):
alpha=((6*(-1)**i-30)/(i**2*np.pi**2))
e=np.exp((-(np.pi**2)*(i**2)*t))
sin=np.sin((i*np.pi*x)/3)
u+=alpha*e*sin
return u
Are you sure about the Fourier coefficients? The number 30 in it is for me somewhat suspicious. Also the frequency seems strange, the continuation of u(x,0) should be an odd rectangular wave of period 8. Notice, it is a=3 but L=4.
I'm trying to show my 2D data on a 3D space.
Here is my code below:
import numpy as np
import matplotlib.pyplot as plt
i = 60
n = 1000
r = 3.8
eps = 0.7
y = np.ones((n, i))
# random numbers on the first row of array x
np.random.seed(1)
x = np.ones((n+1, i))
x[0, :] = np.random.random(i)
def logistic(r, x):
return r * x * (1 - x)
present_indi = np.arange(i)
next_indi = (present_indi + 1) % i
prev_indi = (present_indi - 1) % i
for n in range(1000):
y[n, :] = logistic(r, x[n, :])
x[n+1, :] = (1-eps)*y[n, present_indi] + 0.5*eps*(y[n, prev_indi] + y[n, next_indi])
#print(x)
# the above logic generates a 2D array 'x'. with i columns and n rows.
fig, ax = plt.subplots()
for i in range(60):
for n in range(1000):
if n>=900:
ax.plot(i,x[n,i],'*k',ms=0.9)
plt.xlabel('i')
plt.ylabel('x')
plt.title('test')
plt.show()
The above code perfectly displays i and x graph. I have plotted all the elements of 1st column of X, then all elements of second column, then the third and so on....., using the nested for loop logic (refer to the code)
Now what I need to do is, extend the plotting to 3D, i.e use Xaxis = i, Yaxis= n, Zaxis= array 'x'
I want to plot something like this:
I know I have to use mplot3D
But doing the following won't give me any result:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for i in range(60):
for n in range(1000):
if n>=900:
ax.plot_wireframe(i,n,x[n,i],rstride=1,cstride=1)
Plotting 3d images in matplotlib is a little tricky. Generally you plot whole surfaces at once instead of plotting one line at a time. You do so by passing three 2d arrays, one for each position dimension (x, y, z). But you can't just pass any old 2d arrays either; the points themselves have to be in a precise order!
Sometimes you can do something that just works, but I find it easier to explicitly parameterize plots using u and v dimensions. Here's what I was able to get working here:
# Abstract u and v parameters describing surface coordinates
u_plt = np.arange(x.shape[1])
v_plt = np.arange(x.shape[0])
# The outer products here produce 2d arrays. We multiply by
# ones in this case for an identity transformation, but in
# general, you could use any broadcasted operation on `u`
# and `v`.
x_plt = np.outer(np.ones(np.size(v_plt)), u_plt)
y_plt = np.outer(v_plt, np.ones(np.size(u_plt)))
# In this case, our `x` array gives the `z` values directly.
z_plt = x
fig = plt.figure(figsize=(16, 10))
ax = fig.add_subplot(111, projection='3d')
ax.set_zmargin(1) # Add a bit more space around the plot.
ax.plot_wireframe(x_plt, y_plt, z_plt,
rstride=1, cstride=1, # "Resolution" of the plot
color='blue', linewidth=1.0,
alpha=0.7, antialiased=True)
# Tilt the view to match the example.
ax.view_init(elev = 45, azim = -45)
plt.xlabel('i')
plt.ylabel('x')
plt.title('test')
plt.show()
And here's the resulting image. I had to reduce n to 80 to make this comprehensible at all, and I have no idea what I am looking at, so I am not sure it's correct. But I think it looks broadly similar to the example you gave.
Just to illustrate the power of this approach, here's a nautilus shell. It uses a two-stage parameterization, which could be compressed, but which I find conceptually clearer:
n_ticks = 100
# Abstract u and v parameters describing surface coordinates
u_plt = np.arange(n_ticks // 2) * 2
v_plt = np.arange(n_ticks)
# theta is the angle along the leading edge of the shell
# phi is the angle along the spiral of the shell
# r is the distance of the edge from the origin
theta_plt = np.pi * ((u_plt / n_ticks) * 0.99 + 0.005)
phi_plt = np.pi * v_plt / (n_ticks / 5)
r_plt = v_plt / (n_ticks / 5)
# These formulas are based on the formulas for rendering
# a sphere parameterized by theta and phi. The only difference
# is that r is variable here too.
x_plt = r_plt[:, None] * np.cos(phi_plt[:, None]) * np.sin(theta_plt[None, :])
y_plt = r_plt[:, None] * np.sin(phi_plt[:, None]) * np.sin(theta_plt[None, :])
z_plt = r_plt[:, None] * \
(np.ones(np.shape(phi_plt[:, None])) * np.cos(theta_plt[None, :]))
# This varies the color along phi
colors = cm.inferno(1 - (v_plt[:, None] / max(v_plt))) * \
np.ones(np.shape(u_plt[None, :, None]))
fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot(111, projection='3d')
ax.set_zmargin(1)
ax.plot_surface(x_plt, y_plt, z_plt,
rstride=1, cstride=1,
facecolors=colors, linewidth=1.0,
alpha=0.3, antialiased=True)
ax.view_init(elev = 45, azim = -45)
plt.show()
Let M and N be to integers. One is given one vector s of size (M,1), one vector p of size (N,1) and one matrix u0 of size (M,N).
Assume one wants to 3D-plot (s,p) as the grid and u0 as the function to plot (the z axis). When one writes:
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(s, p, u0, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
the terminal displays the following error message:
ValueError: shape mismatch: objects cannot be broadcast to a single shape
Yet, sizes do match! Is there a way 3D-plotting u0 over the grid (s,p) without using the arange function for the grid (cf the too much recurrent example at https://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#d-plots-in-3d)?
EDIT: Here a MCVE. The original problem is exactly the one encountered with the following example.
import math
from math import *
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
from pylab import plot, axis, savefig, show, title, meshgrid, cm, imshow, contour, clabel, colorbar
from numpy import exp
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
#
M = 10
N = 20
h = 1/float(M)
l = 1/float(N)
#
s = np.zeros((M + 1, 1))
p = np.zeros((N + 1, 1))
for j in range(0, M + 1):
s[j] = 0.0 + 5.0*j*h
for k in range(0, N + 1):
p[k] = 0.0 + 20.0*k*l
#
u0 = np.zeros((M + 1, N + 1))
for j in range(0, M):
for k in range(0, N):
u0[j, k] = exp(-(s[j] + p[k] - 10)**2)
#
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(p, s, u0, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
Ok, I am not sure if the output is the correct plot but here is the solution. Add the following line before u0 = np.zeros((M + 1, N + 1))
sv, pv = np.meshgrid(s, p) # creating a meshgrid of MxN points
and replace your plotting command by
surf = ax.plot_surface(sv, pv, u0.T, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
Output
I am getting a horrible fit when I am trying to fit a parabola to this data.
I am initially making a histogram of the data which is the position of an object and then plotting the negative log values of the histogram bin counts to the position using a parabola fit.
the code I am using is this:
time,pos=postime()
plt.plot(time, pos)
poslen=len(pos)
plt.xlabel('Time')
plt.ylabel('Positions')
plt.show()
n,bins,patches = plt.hist(pos,bins=100)
n=n.tolist()
plt.show()
l=len(bins)
s=len(n)
posx=[]
i=0
j=0
pbin=[]
sig=[]
while j < (l-1):
pbin.append((bins[j]+bins[j+1])/2)
j=j+1
while i < s:
if n[i]==0:
pbin[i]=0
else:
sig.append(np.power(1/n[i],2))
n[i]=n[i]/poslen
n[i]=np.log(n[i])
n[i]=n[i]*(-1)
i=i+1
n[:]=[y for y in n if y != 0]
pbin[:]=[y for y in pbin if y != 0]
from scipy.optimize import curve_fit
def parabola(x, a , b):
return a * (np.power(x,2)) + b
popt, pcov = curve_fit(parabola, pbin, n)
print popt
plt.plot(pbin,n)
plt.plot(pbin, parabola(pbin, *popt), 'r-')
I am not sure why you are computing the histogram... But here is a working example which does not require histogram computation.
import numpy as np
from scipy.optimize import curve_fit
from matplotlib import pyplot
time_ = np.arange(-5, 5, 0.1)
pos = time_**2 + np.random.rand(len(time_))*5
def parabola(x, a, b):
return a * (np.power(x, 2)) + b
popt, pcov = curve_fit(parabola, time_, pos)
yfit = parabola(time_, *popt)
pyplot.plot(time_, pos, 'o')
pyplot.plot(time_, yfit)
Also, if your time_ vector is not uniformly sampled, and you want it to be uniformly sampled for the fit, you can do: fittime_ = np.linsapce(np.min(time_), np.max(time_)) and then yfit = parabola(fittime_, *popt).
You can also use matrix inversion.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5,5,100)
Y = (np.power(x,2) + np.random.normal(0,1,x.shape)).reshape(-1,1)
X = np.c_[np.ones(x.shape), x, np.power(x,2)]
A = np.linalg.inv(X.transpose().dot(X)).dot(X.transpose().dot(Y))
Yp = X.dot(A)
fig = plt.figure()
ax = fig.add_subplot()
plt.plot(x,Y,'o',alpha=0.5)
plt.plot(x,Yp)
plt.show()
The matrix form is
X*A=Y
A=(Xt*X)-1*Xt*Y
You can have a better idea here if needed. It does not always work out and you may want to apply some form of regularization.
I would like to visually plot a 3D graph of the error function calculated for a given slope and y-intercept for a linear regression.
This graph will be used to illustrate a gradient descent application.
Let’s suppose we want to model a set of points with a line. To do this we’ll use the standard y=mx+b line equation where m is the line’s slope and b is the line’s y-intercept. To find the best line for our data, we need to find the best set of slope m and y-intercept b values.
A standard approach to solving this type of problem is to define an error function (also called a cost function) that measures how “good” a given line is. This function will take in a (m,b) pair and return an error value based on how well the line fits the data. To compute this error for a given line, we’ll iterate through each (x,y) point in the data set and sum the square distances between each point’s y value and the candidate line’s y value (computed at mx+b). It’s conventional to square this distance to ensure that it is positive and to make our error function differentiable. In python, computing the error for a given line will look like:
# y = mx + b
# m is slope, b is y-intercept
def computeErrorForLineGivenPoints(b, m, points):
totalError = 0
for i in range(0, len(points)):
totalError += (points[i].y - (m * points[i].x + b)) ** 2
return totalError / float(len(points))
Since the error function consists of two parameters (m and b) we can visualize it as a two-dimensional surface.
Now my question, how can we plot such 3D-graph using python ?
Here is a skeleton code to build a 3D plot. This code snippet is totally out of the question context but it show the basics for building a 3D plot.
For my example i would need the x-axis being the slope, the y-axis being the y-intercept and the z-axis, the error.
Can someone help me build such example of graph ?
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import random
def fun(x, y):
return x**2 + y
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = y = np.arange(-3.0, 3.0, 0.05)
X, Y = np.meshgrid(x, y)
zs = np.array([fun(x,y) for x,y in zip(np.ravel(X), np.ravel(Y))])
Z = zs.reshape(X.shape)
ax.plot_surface(X, Y, Z)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()
The above code produce the following plot, which is very similar to what i am looking for.
Simply replace fun with computeErrorForLineGivenPoints:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import collections
def error(m, b, points):
totalError = 0
for i in range(0, len(points)):
totalError += (points[i].y - (m * points[i].x + b)) ** 2
return totalError / float(len(points))
x = y = np.arange(-3.0, 3.0, 0.05)
Point = collections.namedtuple('Point', ['x', 'y'])
m, b = 3, 2
noise = np.random.random(x.size)
points = [Point(xp, m*xp+b+err) for xp,err in zip(x, noise)]
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ms = np.linspace(2.0, 4.0, 10)
bs = np.linspace(1.5, 2.5, 10)
M, B = np.meshgrid(ms, bs)
zs = np.array([error(mp, bp, points)
for mp, bp in zip(np.ravel(M), np.ravel(B))])
Z = zs.reshape(M.shape)
ax.plot_surface(M, B, Z, rstride=1, cstride=1, color='b', alpha=0.5)
ax.set_xlabel('m')
ax.set_ylabel('b')
ax.set_zlabel('error')
plt.show()
yields
Tip: I renamed computeErrorForLineGivenPoints as error. Generally, there is no need to name a function compute... since almost all functions compute something. You also do not need to specify "GivenPoints" since the function signature shows that points is an argument. If you have other error functions or variables in your program, line_error or total_error might be a better name for this function.