Plotting a masked surface plot using python, numpy and matplotlib - python

I'm plotting a surface using matplotlib 1.1.0.
The plot Z axis is masked like so:
Zm = ma.masked_where((abs(z_grid) < 1.09) & (abs(z_grid) > 0.91), (z_surface))
surf = ax.plot_surface(X, Y,Zm, rstride=2, cstride=2, cmap=colors,linewidth=0, antialiased=False)
But I'm not seeing the mask applied on the plot. I plotted the mask itself as a subplot
surf = ax.plot_surface(X, Y,ma.getmask(Zm), rstride=2, cstride=2, cmap=colors,linewidth=0, antialiased=False)
Which worked, so I know my mask does actually contain True values.
Full code:
from pylab import *
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
import numpy
from mpl_toolkits.mplot3d.axes3d import Axes3D
from matplotlib import patches
from matplotlib.figure import Figure
from matplotlib import rcParams
fig = plt.figure(figsize=plt.figaspect(0.5))
ax = fig.add_subplot(1, 2, 1,projection='3d')
pole_positions_orig = [-0.6+0.73j];
zero_positions_orig = [0.29-0.41j];
surface_limit = 1.7;
min_val = -surface_limit;
max_val = surface_limit;
surface_resolution = 0.0333;
X = numpy.arange(min_val,max_val,surface_resolution)
Y = numpy.arange(min_val,max_val,surface_resolution)
X, Y = numpy.meshgrid(X, Y)
z_grid = X + Y*1j;
z_surface = z_grid*0;
pole_positions = numpy.round(pole_positions_orig,1) + surface_resolution/2+(surface_resolution/2)*1j;
zero_positions = numpy.round(zero_positions_orig,1) + surface_resolution/2 +(surface_resolution/2)*1j;
for k in range(0, len(zero_positions)):
z_surface = z_surface + 20*log10((z_grid - zero_positions[k].real - zero_positions[k].imag*1j));
z_surface = z_surface + 20*log10((z_grid - zero_positions[k].real + zero_positions[k].imag*1j));
for k in range(0, len(pole_positions)):
z_surface = z_surface - 20*log10((z_grid - pole_positions[k].real - pole_positions[k].imag*1j));
z_surface = z_surface - 20*log10((z_grid - pole_positions[k].real + pole_positions[k].imag*1j));
colors = cm.jet;
colors.set_bad('k');
Zm = ma.masked_where((abs(z_grid) < 1.09) & (abs(z_grid) > 0.91), (z_surface))
z_surface = Zm;
surf = ax.plot_surface(X, Y,z_surface, rstride=2, cstride=2, cmap=colors,linewidth=0, antialiased=False)
ticks = [-1, 1];
z_ticks = [-30,-20,-10,0,10,20,30];
ax.set_xticks(ticks);
ax.set_yticks(ticks);
ax.set_zticks(z_ticks);
ax.set_xlabel('Re')
ax.set_ylabel('Im')
ax.set_zlabel('Mag(db)',ha='left')
plt.setp(ax.get_zticklabels(), fontsize=7)
plt.setp(ax.get_xticklabels(), fontsize=7)
plt.setp(ax.get_yticklabels(), fontsize=7)
ax = fig.add_subplot(1, 2, 2,projection='3d')
surf = ax.plot_surface(X, Y,ma.getmask(z_surface), rstride=2, cstride=2, cmap=colors,linewidth=0, antialiased=False)
ax.grid(b=None);
show();
This is what I have:
This is what I want (from matlab):
What am I missing?

Fraxel mentioned that surface_plot doesn't support masking. In order to get around the issue, this is what I did:
I basically manually masked the z axis data by setting every masked value to numpy.nan like so:
Zm = ma.masked_where((abs(z_grid) < 1.02) & (abs(z_grid) > 0.98), (z_surface))
z_surface[where(ma.getmask(Zm)==True)] = numpy.nan
However, it messed up my colormap scaling. To fix that, I did this:
cmap = cm.jet
lev = numpy.arange(-30,30,1);
norml = colors.BoundaryNorm(lev, 256)
surf = ax.plot_surface(X, Y, z_surface,...,norm = norml)
Not 100% what I wanted, but a good compromise nonetheless.

You can do it, but you need to do it by manually colouring the surface faces yourself;
the cmap function takes a nubmer between 0 and 1, so we just need to normalise the values before calling the cmap function on them.
z_surface = numpy.real(z_surface)
min_z, max_z = z_surface.min(), z_surface.max()
colours = numpy.zeros_like(z_surface, dtype=object)
for i in range(len(z_surface)):
for j in range(len(z_surface[0])):
if 0.91 < numpy.sqrt(X[i,j]**2 + Y[i,j]**2) < 1.09:
colours[i,j] = "red"
else:
colours[i,j] = plt.get_cmap("jet")((z_surface[i,j]-min_z) / (max_z - min_z))
surf = ax.plot_surface(X, Y, z_surface, rstride=2, cstride=2, facecolors=colours, linewidth=0, antialiased=False)
I should also point out that matplotlib is casting your z array to real - whether or not you are taking advantage of this on purpose though i don't know.

Related

Plotting slices in 3D as heatmap

How can I visualize 4d data on python, for example i have data like this :
x,y,z = np.mgrid[0:10:10j,20:50:30j,-10:5:15j]
t = np.random.random((10,30,15))
and i want to visualize the data like this :
ps : i have try to use slice function on matlab like this
[x,y,z] = meshgrid(0:1:9,20:1:49,-10:1:4)
temp = rand(30,10,15);
xslice = 5; %can add more slice
yslice = 35;
zslice = 0;
slice(x, y, z, temp, xslice, yslice, zslice)
You can use plot_surface as proposed in this answer in a function like this:
import numpy as np
import scipy.interpolate
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Plot slices of the data at the given coordinates
def plot_slices(x, y, z, data, xslice, yslice, zslice, ax=None):
if ax is None:
ax = plt.figure().add_subplot(111, projection='3d')
# Normalize data to [0, 1] range
vmin, vmax = data.min(), data.max()
data_n = (data - vmin) / (vmax - vmin)
# Take slices interpolating to allow for arbitrary values
data_x = scipy.interpolate.interp1d(x, data, axis=0)(xslice)
data_y = scipy.interpolate.interp1d(y, data, axis=1)(yslice)
data_z = scipy.interpolate.interp1d(z, data, axis=2)(zslice)
# Pick color map
cmap = plt.cm.plasma
# Plot X slice
xs, ys, zs = data.shape
xplot = ax.plot_surface(xslice, y[:, np.newaxis], z[np.newaxis, :],
rstride=1, cstride=1, facecolors=cmap(data_x), shade=False)
# Plot Y slice
yplot = ax.plot_surface(x[:, np.newaxis], yslice, z[np.newaxis, :],
rstride=1, cstride=1, facecolors=cmap(data_y), shade=False)
# Plot Z slice
zplot = ax.plot_surface(x[:, np.newaxis], y[np.newaxis, :], np.atleast_2d(zslice),
rstride=1, cstride=1, facecolors=cmap(data_z), shade=False)
return xplot, yplot, zplot
You would then use it like this:
import numpy as np
np.random.seed(0)
x = np.linspace(0, 10, 10)
y = np.linspace(20, 50, 30)
z = np.linspace(-10, 5, 15)
t = np.random.random((10, 30, 15))
ax = plt.figure().add_subplot(111, projection='3d')
plot_slices(x, y, z, t, 5, 35, 0, ax=ax)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
Output:
Unfortunately, Matplotlib doesn't handle intersecting 3D objects well and clipping is incorrect, but that is a different kind of issue.

3D plot of the CONE using matplotlib

I'm looking for help to draw a 3D cone using matplotlib.
My goal is to draw a HSL cone, then base on the vertex coordinats i will select the color.
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
theta1 = np.linspace(0, 2*np.pi, 100)
r1 = np.linspace(-2, 0, 100)
t1, R1 = np.meshgrid(theta1, r1)
X1 = R1*np.cos(t1)
Y1 = R1*np.sin(t1)
Z1 = 5+R1*2.5
theta2 = np.linspace(0, 2*np.pi, 100)
r2 = np.linspace(0, 2, 100)
t2, R2 = np.meshgrid(theta2, r2)
X2 = R2*np.cos(t2)
Y2 = R2*np.sin(t2)
Z2 = -5+R2*2.5
ax.set_xlabel('x axis')
ax.set_ylabel('y axis')
ax.set_zlabel('z axis')
# ax.set_xlim(-2.5, 2.5)
# ax.set_ylim(-2.5, 2.5)
# ax.set_zlim(0, 5)
ax.set_aspect('equal')
ax.plot_surface(X1, Y1, Z1, alpha=0.8, color="blue")
ax.plot_surface(X2, Y2, Z2, alpha=0.8, color="blue")
# ax.plot_surface(X, Y, Z, alpha=0.8)
#fig. savefig ("Cone.png", dpi=100, transparent = False)
plt.show()
HSL CONE
My cone
So my question now is how to define color of each element.
i have found a solution, maybe it will be usefull for others.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
import colorsys
from matplotlib.tri import Triangulation
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
n_angles = 80
n_radii = 20
# An array of radii
# Does not include radius r=0, this is to eliminate duplicate points
radii = np.linspace(0.0, 0.5, n_radii)
# An array of angles
angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)
# Repeat all angles for each radius
angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
# Convert polar (radii, angles) coords to cartesian (x, y) coords
# (0, 0) is added here. There are no duplicate points in the (x, y) plane
x = np.append(0, (radii*np.cos(angles)).flatten())
y = np.append(0, (radii*np.sin(angles)).flatten())
# Pringle surface
z = 1+-np.sqrt(x**2+y**2)*2
print(x.shape, y.shape, angles.shape, radii.shape, z.shape)
# NOTE: This assumes that there is a nice projection of the surface into the x/y-plane!
tri = Triangulation(x, y)
triangle_vertices = np.array([np.array([[x[T[0]], y[T[0]], z[T[0]]],
[x[T[1]], y[T[1]], z[T[1]]],
[x[T[2]], y[T[2]], z[T[2]]]]) for T in tri.triangles])
x2 = np.append(0, (radii*np.cos(angles)).flatten())
y2 = np.append(0, (radii*np.sin(angles)).flatten())
# Pringle surface
z2 = -1+np.sqrt(x**2+y**2)*2
# NOTE: This assumes that there is a nice projection of the surface into the x/y-plane!
tri2 = Triangulation(x2, y2)
triangle_vertices2 = np.array([np.array([[x2[T[0]], y2[T[0]], z2[T[0]]],
[x2[T[1]], y2[T[1]], z2[T[1]]],
[x2[T[2]], y2[T[2]], z2[T[2]]]]) for T in tri2.triangles])
triangle_vertices = np.concatenate([triangle_vertices, triangle_vertices2])
midpoints = np.average(triangle_vertices, axis=1)
def find_color_for_point(pt):
c_x, c_y, c_z = pt
angle = np.arctan2(c_x, c_y)*180/np.pi
if (angle < 0):
angle = angle + 360
if c_z < 0:
l = 0.5 - abs(c_z)/2
#l=0
if c_z == 0:
l = 0.5
if c_z > 0:
l = (1 - (1-c_z)/2)
if c_z > 0.97:
l = (1 - (1-c_z)/2)
col = colorsys.hls_to_rgb(angle/360, l, 1)
return col
facecolors = [find_color_for_point(pt) for pt in midpoints] # smooth gradient
# facecolors = [np.random.random(3) for pt in midpoints] # random colors
coll = Poly3DCollection(
triangle_vertices, facecolors=facecolors, edgecolors=None)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.add_collection(coll)
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
ax.set_zlim(-1, 1)
ax.elev = 50
plt.show()
Inspired from Jake Vanderplas with Python Data Science Handbook, when you are drawing some 3-D plot whose base is a circle, it is likely that you would try:
# Actually not sure about the math here though:
u, v = np.mgrid[0:2*np.pi:100j, 0:np.pi:20j]
x = np.cos(u)*np.sin(v)
y = np.sin(u)*np.sin(v)
and then think about the z-axis. Since viewing from the z-axis the cone is just a circle, so the relationships between z and x and y is clear, which is simply: z = np.sqrt(x ** 2 + y ** 2). Then you can draw the cone based on the codes below:
from mpl_toolkits import mplot3d
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
def f(x, y):
return np.sqrt(x ** 2 + y ** 2)
fig = plt.figure()
ax = plt.axes(projection='3d')
# Can manipulate with 100j and 80j values to make your cone looks different
u, v = np.mgrid[0:2*np.pi:100j, 0:np.pi:80j]
x = np.cos(u)*np.sin(v)
y = np.sin(u)*np.sin(v)
z = f(x, y)
ax.plot_surface(x, y, z, cmap=cm.coolwarm)
# Some other effects you may want to try based on your needs:
# ax.plot_surface(x, y, -z, cmap=cm.coolwarm)
# ax.scatter3D(x, y, z, color="b")
# ax.plot_wireframe(x, y, z, color="b")
# ax.plot_wireframe(x, y, -z, color="r")
# Can set your view from different angles.
ax.view_init(azim=15, elev=15)
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
plt.show()
ax.set_ylabel("y")
ax.set_zlabel("z")
plt.show()
And from my side, the cone looks like:
and hope it helps.

How to get build a consistent discrete colormap/colorbar with "upper" and "lower" values

An image is worth a thousand words :
https://www.harrisgeospatial.com/docs/html/images/colorbars.png
I want to obtain the same color bar than the one on the right with matplotlib.
Default behavior use the same color for "upper"/"lower" and adjacent cell...
Thank you for your help!
Here is the code I have:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors
N = 100
X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)]
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2
fig, ax = plt.subplots(1, 1, figsize=(8, 8))
# even bounds gives a contour-like effect
bounds = np.linspace(-1, 1, 10)
norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256)
pcm = ax.pcolormesh(X, Y, Z,
norm=norm,
cmap='RdBu_r')
fig.colorbar(pcm, ax=ax, extend='both', orientation='vertical')
In order to have the "over"/"under"-color of a colormap take the first/last color of that map but still be different from the last color inside the colormapped range you can get one more color from a colormap than you have boundaries in the BoundaryNorm and use the first and last color as the respective colors for the "over"/"under"-color.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
N = 100
X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)]
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2
fig, ax = plt.subplots(1, 1, figsize=(8, 8))
# even bounds gives a contour-like effect
bounds = np.linspace(-1, 1, 11)
# get one more color than bounds from colormap
colors = plt.get_cmap('RdBu_r')(np.linspace(0,1,len(bounds)+1))
# create colormap without the outmost colors
cmap = mcolors.ListedColormap(colors[1:-1])
# set upper/lower color
cmap.set_over(colors[-1])
cmap.set_under(colors[0])
# create norm from bounds
norm = mcolors.BoundaryNorm(boundaries=bounds, ncolors=len(bounds)-1)
pcm = ax.pcolormesh(X, Y, Z, norm=norm, cmap=cmap)
fig.colorbar(pcm, ax=ax, extend='both', orientation='vertical')
plt.show()
As suggested in my comment you can change the color map with
pcm = ax.pcolormesh(X, Y, Z, norm=norm, cmap='rainbow_r')
That gives:
You can define your own color map as shown here: Create own colormap using matplotlib and plot color scale

How to make my surface plot appear using Axes3D?

I'm trying to create a surface plot using Python Matplotlib. I've read the documentation in an attempt to figure out where my code was wrong or if I've left anything out, but was having trouble.
The code that I've written is
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def computeCost(X, y, theta):
m = len(y)
predictions = np.dot(X, theta)
squareErros = (predictions - y) ** 2
J = (1 / (2 * m)) * sum(squareErrors)
return J
data = np.loadtxt("./data1.txt", delimiter=',')
X = data[:, 0].reshape(-1, 1)
y = data[:, 1].reshape(-1, 1)
m = len(y)
X = np.concatenate((np.ones((m, 1)), X), axis=1)
theta0_vals = np.linspace(-10, 10, 100) # size (100,)
theta1_vals = np.linspace(-1, 4, 100) # size (100,)
J_vals = np.zeros((len(theta0_vals), len(theta1_vals)))
for i in range(len(x_values)):
for j in range(len(y_values)):
t = np.array([theta0_vals[i], theta1_vals[j]]).reshape(-1, 1)
J_vals[i][j] = computeCost(X, y, t) # size (100, 100)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
surf = ax.plot_surface(theta0_vals, theta1_vals, J_vals)
plt.show()
When I invoke plt.show() I get no output. The surface plot that I'm expecting to see is similar to this:
Would anybody be kind enough to let me know where my usage of the surface plot library went wrong? Thank you.
EDIT
I've tried to run the demo code provided here and it works fine. Here's the code for that:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
# Make data.
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
# Customize the z axis.
ax.set_zlim(-1.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
# Add a color bar which maps values to colors.
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
I think I've figured out the issue by changing a couple of the last lines of code from
ax = fig.add_subplot(111, projection='3d')
surf = ax.plot_surface(theta0_vals, theta1_vals, J_vals)
to
ax = plt.axes(projection='3d')
surf = ax.plot_surface(theta0_vals, theta1_vals, J_vals, rstride=1, cstride=1, cmap='viridis', edgecolor='none')
Making this change gives me a surface plot such that:
The link that gave me reference to this was this.

Fixing jagged edges of 3D plot, selecting an appropriate mask

So I have some 3D data that I am able to plot just fine except the edges look jagged.
The relevant code:
import numpy as np
from matplotlib import cm
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
x = np.arange(-1, 1, 0.01)
y = np.arange(-1, 1, 0.01)
x, y = np.meshgrid(x, y)
rho = np.sqrt(x**2 + y**2)
# Attempts at masking shown here
# My Mask
row=0
while row<np.shape(x)[0]:
col=0
while col<np.shape(x)[1]:
if rho[row][col] > 1:
rho[row][col] = None
col=col+1
row=row+1
# Calculate & Plot
z = rho**2
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(x, y, z, rstride=8, cstride=8, cmap=cm.bone, alpha=0.15, linewidth=0.25)
plt.show()
Produces:
This is so close to what I want except the edges are jagged.
If I disable my mask in the code above & replace it with rho = np.ma.masked_where(rho > 1, rho) it gives:
It isn't jagged but not want I want in the corners.
Any suggestions on different masking or plotting methods to get rid of this jaggedness?
Did you consider using polar coordinates (like in this example) ?
Something like:
import numpy as np
from matplotlib import cm
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# create supporting points in polar coordinates
r = np.linspace(0,1.25,50)
p = np.linspace(0,2*np.pi,50)
R,P = np.meshgrid(r,p)
# transform them to cartesian system
x, y = R * np.cos(P), R * np.sin(P)
rho = np.sqrt(x**2 + y**2)
# Calculate & Plot
z = rho**2
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(x, y, z, rstride=1, cstride=1, cmap=cm.bone, alpha=0.15, linewidth=0.25)
plt.show()

Categories