Plotting multiple 2d curves with matplotlib in 3d - python

I'm trying to plot a fourier series of a triangular wave with matplotlib.
I've managed to plot the elements on top of each other in 2d, but I'd like to plot them in 3d instead, as that makes it more easy to see.
Here's the plot my current code generates
triangular wave
Here's an image of what I'd like to plot, but for the triangular wave instead of a square wave.
square wave
Here's the current code
%matplotlib inline
import numpy as np
from matplotlib import pyplot as plt
import scipy as sp
x1 = np.arange(0, L / 2.0, 0.01)
x2 = np.arange(L/2.0,L,0.01)
x = np.concatenate((x1,x2))
y1 = 2* x1
y2 = 2*(1 - x2)
triangle_y = np.concatenate((y1,y2))
L = 1;
def triangle_function(x, L):
'''given x, returns y as defined by the triangle function defined in the range 0 <= x <= L
'''
if x< 0:
print 'Error: the value of x should be between 0 and L'
y = None
elif x<L/2.0:
y = 2*x
elif x <= L:
y = 2*(1 - x)
else:
print 'Error: the value of x should be between 0 and L'
y = None
return y
def projection_integrand(x, n, L):
'''The inputs to the function are:
x ---> vector of x values.
n ---> the n-number associated to the sine functions
L --> L, upper limit of integration
'''
sine_function = np.sin(n * np.pi * x / np.double(L)) # this is the sine function sin(n*pi*x/L)
integrand = (2.0 / L) * sine_function * triangle_function(x, L) # this is the product of the two functions, with the 2/L factor
#return(sine_function*f_x)
return integrand
from scipy.integrate import quad
n_max = 5
x = np.arange(0, L, 0.01) # x vector
triangle_approx = np.zeros(len(x))
func_list = []
for n in range(1, n_max + 1):
c_n = quad(projection_integrand, 0, L, (n, L))
sin_arg = n* np.pi*x/np.double(L)
current = c_n[0]* np.sin(sin_arg)
triangle_approx += current
func_list.append(current)
from mpl_toolkits.mplot3d import Axes3D
plt.hold(True)
plt.plot(x, func_list[0])
plt.plot(x, func_list[1])
plt.plot(x, func_list[2])
plt.plot(x, func_list[3])
plt.plot(x, func_list[4])
plt.plot(x, triangle_approx)
plt.plot(x, triangle_y)
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('approximating the triangle function as a sum of sines, n = 1 ...' + str(n_max))
plt.legend(['approximation', 'triangle function'])
plt.show()

I have found a way based on this matplotlib official example.
Add this code below your code and you will get something close to what you want:
fig = plt.figure()
ax = fig.gca(projection='3d')
z = np.array([1.0 for point in x])
for n, armonic in enumerate(func_list):
ax.plot(x, armonic, z*n, label='armonic{}'.format(n))
ax.legend()
plt.show()

Related

3D plane in Python

I used the following code to try and plot a 3D plane of on its x axis the height (is also the momentum arm) and on its y axis the mass (in kg) which has a linear connection with the force used. The Z axis is the resulting momentum.
Unfortunately I get the following error:
ValueError: Argument Z must be 2-dimensional.
However I do believe that Z, thus the momentum is dependent on both the mass and the height, thus is Z 2d.
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits import mplot3d
g = 9.81
m = np.linspace(0, 1, 301)
H_1 = np.arange(100, 401, 1)
for i, kg in enumerate(m):
    Fg = -m[i]*g
    M = np.zeros(len(H_1))
    for i, mm in enumerate(H_1):
        F1 = np.array([0, Fg])
        F2 = np.array([Fg * np.sin(np.arctan(200 / H_1[i])), Fg * np.cos(np.arctan(200 / H_1[i]))])
        Fres = np.add(F1, F2)
        M_arm = np.array([0, H_1[i]])
        M[i] = np.cross(M_arm, Fres)/10e3
x,y = np.meshgrid(H_1,m)
z = M
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.plot_surface(x, y, z)
ax.set_xlabel('hoogte toren in (mm)')
ax.set_ylabel('massa')
ax.set_zlabel('momentum')
plt.show()
Check this out:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits import mplot3d
g = 9.81
m = np.linspace(0, 1, 301)
H_1 = np.arange(100, 401, 1)
Fg = -m * g
M = np.zeros(len(H_1))
for i, mm in enumerate(H_1):
F1 = np.array([0, Fg[i]])
F2 = np.array([Fg[i] * np.sin(np.arctan(200 / H_1[i])), Fg[i] * np.cos(np.arctan(200 / H_1[i]))])
Fres = np.add(F1, F2)
M_arm = np.array([0, H_1[i]])
M[i] = np.cross(M_arm, Fres) / 10e3
x, y = np.meshgrid(H_1, m)
z = M
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.plot_surface(x, y, z)
ax.set_xlabel('hoogte toren in (mm)')
ax.set_ylabel('massa')
ax.set_zlabel('momentum')
plt.show()
I made the following changes to your code:
Moved the calculation of Fg outside the loop.
Used the correct index i to access the elements of m and Fg in the loop.
Used the correct array index i to access the elements of H_1 in the loop.
Removed the unnecessary enumerate call in the inner loop.
Used the correct array index i to access the elements of M in the loop.
These changes should fix the errors in your code and produce the expected plot.
:)

How to plot the equation for a semicircle

My half circle doesn't really look like how I expected.
Am I doing this right or am I missing something pretty big here ?
import math
import numpy as np
import matplotlib.pyplot as plt
coord_list = []
h = 0
k = 0
r = 6
for x in range((1 + h - r), (h + r - 1), 1):
y1 = k + math.sqrt(r**2 - (x-h)**2)
coord_list.append([x, y1])
for each in coord_list:
print(each)
data = np.array([coord_list])
x, y = data.T
figure = plt.scatter(x, y)
figure = plt.grid(color = 'green', linestyle = '--', linewidth = 0.2)
figure = plt.show()
Use np.linspace to create an array for the x values. Use many points to create the circle
Use np.sqrt to solve for an array, instead of looping through each value.
import numpy as np
import matplotlib.pyplot as plt
# function for semicircle
def semicircle(r, h, k):
x0 = h - r # determine x start
x1 = h + r # determine x finish
x = np.linspace(x0, x1, 10000) # many points to solve for y
# use numpy for array solving of the semicircle equation
y = k + np.sqrt(r**2 - (x - h)**2)
return x, y
x, y = semicircle(6, 0, 0) # function call
plt.scatter(x, y, s=3, c='turquoise') # plot
plt.gca().set_aspect('equal', adjustable='box') # set the plot aspect to be equal
Answering my own question.
Plotting the output of the code:
import math
import numpy as np
import matplotlib.pyplot as plt
coord_list = []
h = 0
k = 0
r = 6
for x in range((1 + h - r), (h + r - 1), 1):
y1 = k + math.sqrt(r**2 - (x-h)**2)
coord_list.append([x, y1])
for each in coord_list:
print(each)
data = np.array([coord_list])
x, y = data.T
figure = plt.scatter(x, y)
figure = plt.grid(color = 'green', linestyle = '--', linewidth = 0.2)
figure = plt.show()
[-5, 3.3166247903554]
[-4, 4.47213595499958]
[-3, 5.196152422706632]
[-2, 5.656854249492381]
[-1, 5.916079783099616]
[0, 6.0]
[1, 5.916079783099616]
[2, 5.656854249492381]
[3, 5.196152422706632]
[4, 4.47213595499958]
Looking at the output of the coordinates, it doesn't appear to be a circle.
But if we take our equation and our coordinates and graph them on this website we see that they are indeed a circle. It's an optical illusion that they aren't. Partially because the graph is not evenly displayed and also because plotting points in a range function with steps of 1 (line 13) doesn't plot points at equal arc length distances away from each other.

Python only plots last curve in a loop

I have a curve that I want to rotate along the z-axis N times in a loop, and each time I want to plot it to the same figure. However I only get the last curve overdrawn multiple times. If instead, I use different figures in every iteration I get the expected result. Where is the mistake? This is the code, the comments are to test creating new figures in each iteration:
o = 2*pi/N
for m in range(N):
#fig = plt.figure()
#ax = fig.add_subplot(111, projection='3d')
#ax.view_init(45,)
#fig.set_size_inches(10,10)
for n in range(len(x1)):
x1[n],y1[n] = cos(o)*x1[n] - sin(o)*y1[n], sin(o)*x1[n] + cos(o)*y1[n]
ax.plot(x1,y1,z1,'k')
#plt.show()
This is what I want more or less, that I've achieved using a diferent method:
but instead i get:
Here is how I calculate x1,y1,z1 if someone wants to test it
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from numpy import array, linspace
from math import pi, cos, sin, atan2, sqrt
def pltcono(xo,yo,a):
N = 100
x1 = []
y1 = []
z1 = []
for t in linspace(0,1,N):
x = (xo[1]-xo[0])*t + xo[0]
y = (yo[1]-yo[0])*t + yo[0]
r = sqrt(x**2 + y**2)
if (r > 1.0000000001):
return x1,y1,z1
o = atan2(y,x)
x1 += [a*r/(2*pi)*cos(2*pi*o/a)]
y1 += [a*r/(2*pi)*sin(2*pi*o/a)]
z1 += [-r*sqrt(1-(a/(2*pi))**2)]
return x1,y1,z1
def cono(a):
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.view_init(45,)
fig.set_size_inches(10,10)
o = a/2
r = 1
N = 10
p = a/10
x = [r*cos(p)]
y = [r*sin(p)]
x += [r*sin(o-p)/sin(o)]
y += [0]
x1,y1,z1 = pltcono(x,y,a)
while (o + a < pi):
o+= a
x[0] = x[1]*cos(a)
y[0] = x[1]*sin(a)
x[1] = r*sin(a/2-p)/sin(o)
xt,yt,zt = pltcono(x,y,a)
x1 += xt
y1 += yt
z1 += zt
x[0] = x[1]*cos(a)
y[0] = x[1]*sin(a)
x[1] = x[0] - cos(o+a)
y[1] = y[0] - sin(o+a)
xt,yt,zt = pltcono(x,y,a)
x1 += xt
y1 += yt
z1 += zt
x1 = array(x1)
y1 = array(y1)
z1 = array(z1)
o = 2*pi/N
for m in range(N):
#fig = plt.figure()
#ax = fig.add_subplot(111, projection='3d')
#ax.view_init(45,)
#fig.set_size_inches(10,10)
for n in range(len(x1)):
x1[n],y1[n] = cos(o)*x1[n] - sin(o)*y1[n], sin(o)*x1[n] + cos(o)*y1[n]
ax.plot(x1,y1,z1,'k')
#plt.show()
cono(pi/6+pi/24+0.001)
Note that you need to create a figure every time or pyplot will plot in the first one created.
import math
import matplotlib.pyplot as plt
o = 2*pi/N
for m in range(N):
for n in range(len(x1)):
x1[n],y1[n] = math.cos(o)*x1[n] - math.sin(o)*y1[n], math.sin(o)*x1[n] + math.cos(o)*y1[n]
plt.figure()
plt.plot(x1,y1,z1,'k')
I assigned random values to x1,y1,z1,N and got the following output:
It is not the whole output, just signifies multiple plots.
Look here for more ways to do the same.

Fourier Transform Using Numpy

I'm trying to calculate the Fourier Transform of the following Gaussian:
# sample spacing
dx = 1.0 / 1000.0
# Points
x1 = -5
x2 = 5
x = np.arange(x1, x2, dx)
def light_intensity():
return 10*sp.stats.norm.pdf(x, 0, 1)+0.1*np.random.randn(x.size)
fig, ax = plt.subplots()
ax.plot(x,light_intensity())
I create a new array in the spacial frequency domain (Fourier Transform of Gaussian is a Gaussian so these values should be similar). I plot and get this:
fig, ax = plt.subplots()
xf = np.arange(x1,x2,dx)
yf= np.fft.fftshift(light_intensity())
ax.plot(xf,np.abs(yf))
Why is it splitting into two peaks?
Advice:
use np.fft.fft
fft starts at 0 Hz
normalize/rescale
Complete example:
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
def norm_fft(y, T, max_freq=None):
N = y.shape[0]
Nf = N // 2 if max_freq is None else int(max_freq * T)
xf = np.linspace(0.0, 0.5 * N / T, N // 2)
yf = 2.0 / N * np.fft.fft(y)
return xf[:Nf], yf[:Nf]
def generate_signal(x, signal_gain=10.0, noise_gain=0.0):
signal = norm.pdf(x, 0, 1)
noise = np.random.randn(x.size)
return signal_gain * signal + noise_gain * noise
# Signal parameters
x1 = 0.0
x2 = 5.0
N = 10000
T = x2 - x1
# Generate signal data
x = np.linspace(x1, x2, N)
y = generate_signal(x)
# Apply FFT
xf, yf = norm_fft(y, T, T / np.pi)
# Plot
fig, ax = plt.subplots(2)
ax[0].plot(x, y)
ax[1].plot(xf, np.abs(yf))
plt.show()
Or, with noise:
Plots with symmetry
Alternatively, if you want to enjoy the symmetry in the frequency domain:
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
def norm_sym_fft(y, T, max_freq=None):
N = y.shape[0]
b = N if max_freq is None else int(max_freq * T + N // 2)
a = N - b
xf = np.linspace(-0.5 * N / T, 0.5 * N / T, N)
yf = 2.0 / N * np.fft.fftshift(np.fft.fft(y))
return xf[a:b], yf[a:b]
def generate_signal(x, signal_gain=10.0, noise_gain=0.0):
signal = norm.pdf(x, 0, 1)
noise = np.random.randn(x.size)
return signal_gain * signal + noise_gain * noise
# Signal parameters
x1 = -10.0
x2 = 10.0
N = 10000
T = x2 - x1
# Generate signal data
x = np.linspace(x1, x2, N)
y = generate_signal(x)
# Apply FFT
xf, yf = norm_sym_fft(y, T, 4 / np.pi)
# Plot
fig, ax = plt.subplots(2)
ax[0].plot(x, y)
ax[1].plot(xf, np.abs(yf))
plt.show()
Or, with noise:
First, use np.fft.fft to computes the Fourier Transform then use np.fft.fftshift to shift the zero-frequency component to the center of the spectrum.
Replace the second part of your code with:
xf = np.arange(x1,x2,dx)
yf = np.fft.fft(light_intensity())
yfft = np.fft.fftshift(np.abs(yf))
fig,ax = plt.subplots(1,2,figsize=(10,5))
ax[0].plot(xf,light_intensity())
ax[1].plot(xf,yfft)
ax[1].set_xlim(-0.05,0.05)
plt.show()
This is the result:

How to extract a 2D plane from a 3D numpy meshgrid

[TLDR]:
Essentially my question boils down to how one can extract the 2d data of a plane from a 3D numpy meshgrid
[Detailed Description]:
I am calculating the electric field of two (or more) point charges. I did this in 2D and can plot the results via matplotlib using quiver or streamplot
import numpy as np
from matplotlib import pyplot as plt
eps_0 = 8e-12
fac = (1./(4*np.pi*eps_0))
charges = [1.0,-1.0]
qx = [-2.0,2.0]
qy = [0.0,0.0]
# GRID
gridsize = 4.0
N = 11
X,Y = np.meshgrid( np.linspace(-gridsize,gridsize,N),
np.linspace(-gridsize,gridsize,N))
# CALC E-FIELD
sumEx = np.zeros_like(X)
sumEy = np.zeros_like(Y)
for q, qxi, qyi in zip(charges,qx,qy):
dist_vec_x = X - qxi
dist_vec_y = Y - qyi
dist = np.sqrt(dist_vec_x**2 + dist_vec_y**2)
Ex = fac * q * (dist_vec_x/dist**3)
Ey = fac * q * (dist_vec_y/dist**3)
sumEx += Ex
sumEy += Ey
# PLOT
fig = plt.figure()
ax = fig.add_subplot(111)
ax.streamplot(X,Y,sumEx,sumEy)
plt.show()
This produces the correct results
I can easily extend this to 3D
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import pyplot as plt
eps_0 = 8e-12
fac = (1./(4*np.pi*eps_0))
charges = [1.0,-1.0]
qx = [-2.0,2.0]
qy = [0.0,0.0]
qz = [0.0,0.0]
# GRID
gridsize = 4.0
N = 11
X,Y,Z = np.meshgrid( np.linspace(-gridsize,gridsize,N),
np.linspace(-gridsize,gridsize,N),
np.linspace(-gridsize,gridsize,N))
# CALC E-FIELD
sumEx = np.zeros_like(X)
sumEy = np.zeros_like(Y)
sumEz = np.zeros_like(Z)
for q, qxi, qyi, qzi in zip(charges,qx,qy,qz):
dist_vec_x = X - qxi
dist_vec_y = Y - qyi
dist_vec_z = Z - qzi
dist = np.sqrt(dist_vec_x**2 + dist_vec_y**2 + dist_vec_z**2)
Ex = fac * q * (dist_vec_x/dist**3)
Ey = fac * q * (dist_vec_y/dist**3)
Ez = fac * q * (dist_vec_z/dist**3)
sumEx += Ex
sumEy += Ey
sumEz += Ez
# PLOT
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.quiver(X,Y,Z,sumEx,sumEy,sumEz, pivot='middle', normalize=True)
plt.show()
This also yields the correct result when plotted in 3D (as far as I can tell)
But for some reason I can not figure out how to extract the data from one x-y plane from the generated 3D numpy mesh. I thought I could just do something like
zplane = round(N/2)
ax.quiver(X,Y,sumEx[:,:,zplane],sumEy[:,:,zplane])
but this does not do the trick. Does anyone know the proper way here?
Remove projection='3d' and index X and Y:
fig = plt.figure()
ax = fig.gca()
zplane = round(N / 2)
ax.quiver(X[:, :, zplane], Y[:, :, zplane], sumEx[:, :, zplane], sumEy[:, :, zplane])
plt.show()
If you select a specific zplane your plot is no longer a 3D-plot.

Categories

Resources