Python surface plot Value error - python

I'm getting used to python and now wanna make a 3D surface plot.
I have three variables x,y,z and an Intensity function I=I(x,y,z). I want to pick out a particular slice z=250 and plot the dsitribution for the x and y direction.
The Problem is, I dont know how to pick a slice for a z-value. All Versions I tried ended iin some error
Here is what i have
import pandas as pd
import numpy as np #NumPy
import scipy as sp #SciPy
import matplotlib as mpl #Matplotlib(2D/3D)
import matplotlib.pyplot as plt #Matplotlib's pyplot
from pylab import * #Matplotlib's pylab
from mpl_toolkits.mplot3d import Axes3D
%matplotlib inline
x = linspace(-4,4,50) # units mm
y = linspace(-4,4,50) # units mm
# define beam parameters
I_0 = 1e0 # intensity
lambda_1 = 800e-9 # wavelength
w_0 = 1.5 # beam waist
z_r = pi*w_0**2*1e-6/lambda_1 # Rayleigh length in units mm
z = linspace(-4,4,500)*z_r
# calculate intensity profile
X,Y,Z = meshgrid(x,y,z)
w = w_0 *sqrt(1+(Z/z_r)**2)
I = I_0*((w_0/w)**2)*exp(-2*(X**2+Y**2)/(w**2))
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, I[:,:,250], cmap=cm.coolwarm,linewidth=0,antialiased=False)
show()
I get an error: ValueError: shape mismatch: objects cannot be broadcast to a single shape
How do I do it correctly?
Edit: I'm using python 3.6 with jupyter notebook

numpy.meshgrid(x,y,z) creates three 3D arrays. Matplotlib will need 2D arrays as input. You would hence need another set of x and y arrays for plotting. Those can also be created via numpy.meshgrid(x,y) or you can just slice the existing arrays.
surf = ax.plot_surface(X[:,:,0], Y[:,:,0], I[:,:,250], ...)
Complete example:
import numpy as np #NumPy
import matplotlib.pyplot as plt #Matplotlib's pyplot
from mpl_toolkits.mplot3d import Axes3D
#%matplotlib inline
x = np.linspace(-4,4,50) # units mm
y = np.linspace(-4,4,50) # units mm
# define beam parameters
I_0 = 1e0 # intensity
lambda_1 = 800e-9 # wavelength
w_0 = 1.5 # beam waist
z_r = np.pi*w_0**2*1e-6/lambda_1 # Rayleigh length in units mm
z = np.linspace(-4,4,500)*z_r
X,Y,Z = np.meshgrid(x,y,z)
w = w_0 *np.sqrt(1+(Z/z_r)**2)
# calculate intensity profile
I = I_0*((w_0/w)**2)*np.exp(-2*(X**2+Y**2)/(w**2))
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X[:,:,0], Y[:,:,0], I[:,:,250],
cmap=plt.cm.coolwarm,linewidth=0,antialiased=False)
plt.show()

Related

How to fill area under 3D circular line plot in Python [duplicate]

I have a 3d plot made using matplotlib. I now want to fill the vertical space between the drawn line and the x,y axis to highlight the height of the line on the z axis. On a 2d plot this would be done with fill_between but there does not seem to be anything similar for a 3d plot. Can anyone help?
here is my current code
from stravalib import Client
import matplotlib as mpl
import numpy as np
import matplotlib.pyplot as plt
... code to get the data ....
mpl.rcParams['legend.fontsize'] = 10
fig = plt.figure()
ax = fig.gca(projection='3d')
zi = alt
x = df['x'].tolist()
y = df['y'].tolist()
ax.plot(x, y, zi, label='line')
ax.legend()
plt.show()
and the current plot
just to be clear I want a vertical fill to the x,y axis intersection NOT this...
You're right. It seems that there is no equivalent in 3D plot for the 2D plot function fill_between. The solution I propose is to convert your data in 3D polygons. Here is the corresponding code:
import math as mt
import matplotlib.pyplot as pl
import numpy as np
import random as rd
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
# Parameter (reference height)
h = 0.0
# Code to generate the data
n = 200
alpha = 0.75 * mt.pi
theta = [alpha + 2.0 * mt.pi * (float(k) / float(n)) for k in range(0, n + 1)]
xs = [1.0 * mt.cos(k) for k in theta]
ys = [1.0 * mt.sin(k) for k in theta]
zs = [abs(k - alpha - mt.pi) * rd.random() for k in theta]
# Code to convert data in 3D polygons
v = []
for k in range(0, len(xs) - 1):
x = [xs[k], xs[k+1], xs[k+1], xs[k]]
y = [ys[k], ys[k+1], ys[k+1], ys[k]]
z = [zs[k], zs[k+1], h, h]
#list is necessary in python 3/remove for python 2
v.append(list(zip(x, y, z)))
poly3dCollection = Poly3DCollection(v)
# Code to plot the 3D polygons
fig = pl.figure()
ax = Axes3D(fig)
ax.add_collection3d(poly3dCollection)
ax.set_xlim([min(xs), max(xs)])
ax.set_ylim([min(ys), max(ys)])
ax.set_zlim([min(zs), max(zs)])
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
pl.show()
It produces the following figure:
I hope this will help you.

Mpl toolkits plot_surface not showing any output

I am trying to plot z transforms of some signals using the mpl_toolkits in python, but the output is totally blank. What am I doing wrong? The input numpy arrays have non-zero values. Here is my code:
import numpy as np
import matplotlib.pyplot as plt
import math
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
# initialize r and theta
r = 10
theta = r*np.linspace(-math.pi,math.pi,100)
theta = np.meshgrid(theta,theta)[0]
# calculate z
z = r*(np.cos(theta) + 1j*np.sin(theta))
# calculate z transform for first signal
xs1 = np.abs(z/(z-2))
# calculate z transform for second signal
xs2 = np.abs((np.power(z,3)+2*np.power(z,2)+3*z+3)/np.power(z,3))
# plot the transforms
fig1 = plt.figure(0)
ax1 = fig1.add_subplot(111, projection='3d')
fig2 = plt.figure(1)
ax2 = fig2.add_subplot(111, projection='3d')
ax1.plot_surface(z.real,z.imag,xs1,cmap = cm.coolwarm)
ax2.plot_surface(z.real,z.imag,xs2,cmap = cm.coolwarm)
plt.show()
Here is one of the output:

Display image of 2D Sinewaves in 3D

I've generated a 1D sine wave and then repeated it every row to have a 2D sine wave. I can show this in 2d space, but I need to produce a 3D plot that shows the peaks and valleys as well as the oscillatory patterns between them.
import numpy as np
import matplotlib.pyplot as plt
N = 256
x = np.linspace(-np.pi,np.pi, N)
sine1D = 128.0 + (127.0 * np.sin(x))
sine1D = np.uint8(sine1D)
sine2D = np.tile(sine1D, (N,1))
plt.imshow(sine2D, cmap='gray')
how about, as #Warren Weckesser said, use the mplot3d toolkit examples gallery, and for instance surface plot of the magnitude of a sinewave as function of time and phase:
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
fig = plt.figure()
ax3d = fig.add_subplot(111, projection='3d')
# Make the X, Y meshgrid instead of np.tile
xs = np.linspace(-2*np.pi, 2*np.pi, 200)
ys = np.linspace(-2*np.pi, 2*np.pi, 200)
tau, phi = np.meshgrid(xs, ys)
# Z evaluation
amp = np.sin(tau+phi)
ax3d.set_xlabel(r'$\tau$') # tau = omega*t -> adimensional time
ax3d.set_ylabel(r'$\phi$') # phi -> phase
ax3d.set_zlabel(r'$amp$') # signal amplitude
surf = ax3d.plot_surface(tau, phi, amp,cmap=cm.inferno)
fig.colorbar(surf)
that gives :

Problems with unpacking Matplotlib hist2d outputs

I'm using Matplotlib's function hist2d() and I want to unpack the output in order to further use it. Here's what I do: I simply load with numpy a 2-column file containing my data and use the following code
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
import numpy as np
traj = np.loadtxt('trajectory.txt')
x = traj[:,0]
y = traj[:,1]
M, xe, ye, img = plt.hist2d(x, y, bins = 80, norm = LogNorm())
plt.imshow(M)
plt.show()
The result I get is the following:
Instead, if I try to directly plot the hist2d results without unpacking them:
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
import numpy as np
traj = np.loadtxt('trajectory.txt')
x = traj[:,0]
y = traj[:,1]
plt.hist2d(x, y, bins = 80, norm = LogNorm())
plt.show()
I get the whole plot without the strange blue box. What am I doing wrong?
You can create a histogram plot directly with plt.hist2d. This calculates the histogram and plots it to the current axes. There is no need to show it yet another time using imshow.
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
import numpy as np; np.random.seed(9)
x = np.random.rayleigh(size=9900)
y = np.random.rayleigh(size=9900)
M, xe, ye, img = plt.hist2d(x, y, bins = 80, norm = LogNorm())
plt.show()
Or, you may first calculate the histogram and afterwards plot the result as an image to the current axes. Note that the histogram produced by numpy is transposed, see Matplotlib 2D histogram seems transposed, making it necessary to call imshow(M.T). Also note that in order to obtain the correct axes labeling, you need to set the imshow's extent to the extremal values of the xe and ye edge arrays.
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
import numpy as np; np.random.seed(9)
x = np.random.rayleigh(size=9900)
y = np.random.rayleigh(size=9900)
M, xe, ye = np.histogram2d(x, y, bins = 80)
extent = [xe[0], xe[-1], ye[0], ye[-1]]
plt.imshow(M.T, extent=extent, norm = LogNorm(), origin="lower")
plt.show()

Plot sphere in matplotlib from non-organised data

I have some Fortran code which outputs the polar coordinates of a grid on the surface of a sphere in theta, phi format. It also outputs a value associated with each of these points (specifically meant to represent the voltage at that point on the sphere's surface).
Now I want to read this data into Python, plot a sphere, and then colour it according to the voltage data values. I know how to do this for a latitude-longitude grid, but my grid points are not ordered in any specific way.
The code I'm trying is as follows:
import matplotlib.pyplot as plt
from matplotlib import cm, colors
from mpl_toolkits.mplot3d import Axes3D
import option_d
import numpy as np
# Create a sphere
r = 1.0
pi = np.pi
cos = np.cos
sin = np.sin
#Read in grid points
data = np.genfromtxt('grid.txt')
phi, theta = np.hsplit(data, 2)
#Convert grid points to cartesian
x = r*sin(phi)*cos(theta)
y = r*sin(phi)*sin(theta)
z = r*cos(phi)
#Import data from initial state
colorfunction = np.genfromtxt('sphere_init.txt')
print np.shape(colorfunction)
#Normalise the colour map to the initial data
newcm = option_d.test_cm
norm=colors.Normalize(vmin = -np.max(colorfunction), vmax = np.max(colorfunction), clip = False)
#Plot the surface
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(
x,y,z,rstride=1,cstride=1,cmap=newcm,facecolors=newcm(norm(colorfunction)))
#Set axes and display or save
ax.set_aspect("equal")
plt.tight_layout()
plt.show()
The file 'grid.txt' contains two columns, each 770 in length, representing the phi, theta coordinates of each point. The file 'sphere_init.txt' contains a single column of length 770, which are the corresponding data values. However, this does not work - it just throws error messages at me. Is it even possible to plot a sphere from disordered grid points? Any help much appreciated. Thanks.
Edit
Here is the error message:
Traceback (most recent call last):
File "sphere.py", line 43, in <module>
x,y,z,rstride=1,cstride=1, cmap=newcm,facecolors=newcm(norm(colorfunction)))
File "/usr/lib/pymodules/python2.7/mpl_toolkits/mplot3d/axes3d.py", line 1611, in plot_surface
colset.append(fcolors[rs][cs])
IndexError: index out of bounds
I believe I have solved my problem. I read in my irregular grid data, and then also create a regular latitude-longitude grid. I then interpolate from the irregular grid to the lat-long grid:
import matplotlib.pyplot as plt
import matplotlib.mlab as ml
from matplotlib import cm, colors
from mpl_toolkits.mplot3d import Axes3D
import option_d
import numpy as np
import time
#Read in lebedev grid points
data = np.genfromtxt('grid.txt')
u, v = np.hsplit(data, 2)
phi, theta = u[:,0], v[:,0]
#Import data from initial state
colorfunction = np.genfromtxt('sphere_init.txt')
#Generate a lat-long grid to interpolate on
p = np.linspace(0,np.pi, 770)
t = np.linspace(-np.pi, np.pi, 770)
p, t = np.meshgrid(p, t)
#Interpolate using delaunay triangularization
zi = ml.griddata(phi, theta, colorfunction, p, t)
#Convert the lat-long grid points to cartesian
x = np.sin(p)*np.cos(t)
y = np.sin(p)*np.sin(t)
z = np.cos(p)
#Normalize the interpolated colourfunction
#Use fancy new colourmap
newcm = option_d.test_cm
norm=colors.Normalize(vmin = -np.max(zi), vmax = np.max(zi), clip = False)
#Plot the surface
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(
x,y,z,rstride=1,cstride=1, cmap=newcm,facecolors=newcm(norm(zi)))
#Display
ax.set_aspect("equal")
ax.set_xlim([-1,1])
ax.set_ylim([-1,1])
ax.set_zlim([-1,1])
plt.tight_layout()
plt.show()
Edit
I have run into a new problem with this method. It causes a chunk to be missing from the back of my sphere:
Any ideas why?

Categories

Resources