This python code allows to plot Z = f(X,Y). My question is what should I modify to plot f(X,Y,Z,data)?
Let's say that data corresponds to temperature, and I have temperature for each location X, Y, and Z.
import numpy as np
import matplotlib.pyplot as pet
from mpl_toolkits.mplot3d import Axes3D
x = np.arange(0,25,1)
y = np.arange(0,25,1)
x,y = np.meshgrid(x,y)
z = x**2 + y**2
fig = plt.figure()
axes = fig.gca(projection='3d')
axes.plot_surface(x,y,z)
plt.show()
I guess that I need to have
x = np.arange(0,25,1) y = np.arange(0,25,1) z = np.arange(0,25,1) x,y,z = np.meshgrid(x,y,z)
but axes.plot_surface(x,y,z) will not be working anymore. What I should use instead?
Related
I have a function of the form f(x,y,z) and want to create a surface plot for it (level sets) using matplotlib. The problem I have is that plot_surface only accepts 3 arguments, whereas the type of plot I want to do is create a grid of x,y,z values and then plot the value of my function f at each of those points.
Here is a minimal example:
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
bounds = [1,1,1]
numpoints = 25
x = np.linspace(-bounds[0], bounds[0], numpoints)
y = np.linspace(-bounds[1], bounds[1], numpoints)
z = np.linspace(-bounds[2], bounds[2], numpoints)
X, Y, Z = np.meshgrid(x, y, z)
s = X.shape
Ze = np.zeros(s)
Zp = np.zeros(s)
DT = np.zeros((numpoints**3,3))
# convert mesh into point vector for which the model can be evaluated
c = 0
for i in range(s[0]):
for j in range(s[1]):
for k in range(s[2]):
DT[c,0] = X[i,j,k]
DT[c,1] = Y[i,j,k]
DT[c,2] = Z[i,j,k]
c = c+1;
# this could be any function that returns a shape (numpoints**3,)
Ep = np.square(DT)[:,0]
c = 0
for i in range(s[0]):
for j in range(s[1]):
for k in range(s[2]):
Zp[i,j,k] = Ep[c]
c = c+1;
Now I would like to plot Zp as level sets in matplotlib. Is this possible?
The only way to represent 4 variables (x, y, x, f(x, y, z)) I could think in matplotlib is scatter the grid of x, y, z and give a color to the points that is proportional to f(x, y, z):
bounds = [1,1,1]
numpoints = 11
x = np.linspace(-bounds[0], bounds[0], numpoints)
y = np.linspace(-bounds[1], bounds[1], numpoints)
z = np.linspace(-bounds[2], bounds[2], numpoints)
X, Y, Z = np.meshgrid(x, y, z)
For exaple let's say taht f(x,y,z)=sin(x+y)+cos(y+z):
f_xyz = np.sin(X+Y)+np.cos(Y+Z)
Now let's scatter:
plt.figure(figsize=(7,7))
ax = plt.subplot(projection="3d")
ax.scatter(X, Y, Z, s=10, alpha=.5, c=f_xyz, cmap="RdBu")
plt.show()
As you can see the result is a bit confusing and not very clear, but it strongly depends on what function you want to plot. I hope you could find a better way
I am trying to use the colormap feature of a 3d-surface plot in matplotlib to color the surface based on values from another array instead of the z-values.
The surface plot is created and displayed as follows:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def gauss(x, y, w_0):
r = np.sqrt(x**2 + y**2)
return np.exp(-2*r**2 / w_0**2)
x = np.linspace(-100, 100, 100)
y = np.linspace(-100, 100, 100)
X, Y = np.meshgrid(x, y)
Z = gauss(X, Y, 50)
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.plot_surface(X, Y, Z, cmap='jet')
Now instead of coloring based on elevation of the 3d-surface, I am looking to supply the color data for the surface in form of another array, here as an example a random one:
color_data = np.random.uniform(0, 1, size=(Z.shape))
However, I did not find a solution to colorize the 3d-surface based on those values. Ideally, it would look like a contourf plot in 3d, just on the 3d surface.
You can use matplotlib.colors.from_levels_and_colors to obtain a colormap and normalization, then apply those to the values to be colormapped.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.colors
x = np.linspace(-100, 100, 101)
y = np.linspace(-100, 100, 101)
X, Y = np.meshgrid(x, y)
Z = np.exp(-2*np.sqrt(X**2 + Y**2)**2 / 50**2)
c = X+50*np.cos(Y/20) # values to be colormapped
N = 11 # Number of level (edges)
levels = np.linspace(-150,150,N)
colors = plt.cm.get_cmap("RdYlGn", N-1)(np.arange(N-1))
cmap, norm = matplotlib.colors.from_levels_and_colors(levels, colors)
color_vals = cmap(norm(c))
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.plot_surface(X, Y, Z, facecolors=color_vals, rstride=1, cstride=1)
plt.show()
I would to know if there is the possibility to plot in four dimensions using python. In particular I would to have a tridimensional mesh X, Y, Z and f(X,Y,Z) = 1 or f(X,Y,Z) = 0.
So I need to a symbol (for example "o" or "x") for some specific point (X,Y,Z).
I don't need to a color scale.
Note that I have 100 matrices (512*512) composed by 1 or 0: so my mesh should be 512*512*100.
I hope I have been clear! Thanks.
EDIT:
This is my code:
X = np.arange(W.shape[2])
Y = np.arange(W.shape[1])
Z = np.arange(W.shape[0])
X, Y, Z = np.meshgrid(X, Y, Z)
fig = plt.figure()
ax = fig.gca(projection='3d')
for z in range(W.shape[0]):
indexes = np.where(W[z])
ax.scatter(X[indexes], Y[indexes], ???, marker='.')
ax.set_xlabel('X = columns')
ax.set_ylabel('Y = rows')
ax.set_zlabel('Z')
plt.show()
W is my tridimensional matrix, so: W[0], W[1], etc are 512x512 matrices.
My question is: what have I to write insted of ??? in my code. I know I shouldn't ask this, but I can't understand the idea.
You could create inspect the value of f(x,y,z) for layers of z to see if they are non-zero or not, and then scatterplot the function based on this.
e.g. for nz layers of (n,n) matrices, each a slice of a sphere:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
n, nz = 48, 24
x, y = np.linspace(-n//2,n//2-1,n), np.linspace(-n//2,n//2-1,n)
X, Y = np.meshgrid(x, y)
def f(x,y,z):
return (X**2 + Y**2 + (z-nz//2)**2) < (n*0.2)**2
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for z in range(nz):
layer = f(X, Y, z)
indexes = np.where(layer)
ax.scatter(X[indexes], Y[indexes], layer[indexes]*(z-nz//2), marker='.')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
For random non-zero elements of f(x,y,z):
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
n, nz = 12, 10
x, y, z = np.linspace(0,n-1,n), np.linspace(0,n-1,n), np.linspace(0,nz-1,nz)
X, Y, Z = np.meshgrid(x, y, z)
f = np.random.randint(2, size=(n,n,nz))
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for z in range(nz):
indexes = np.where(f[...,z])
ax.scatter(X[indexes], Y[indexes], f[indexes]+z, marker='.')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
But with your large arrays, you may run into problems (a) with memory and the speed of the plotting and (b) being able to resolve detail in the "central" block of the plot.
When I plot something with contourf, I see at the bottom of the plot window the current x and y values under the mouse cursor.
Is there a way to see also the z value?
Here an example contourf:
import matplotlib.pyplot as plt
import numpy as hp
plt.contourf(np.arange(16).reshape(-1,4))
The text that shows the position of the cursor is generated by ax.format_coord. You can override the method to also display a z-value. For instance,
import matplotlib.pyplot as plt
import numpy as np
import scipy.interpolate as si
data = np.arange(16).reshape(-1, 4)
X, Y = np.mgrid[:data.shape[0], :data.shape[1]]
cs = plt.contourf(X, Y, data)
func = si.interp2d(X, Y, data)
def fmt(x, y):
z = np.take(func(x, y), 0)
return 'x={x:.5f} y={y:.5f} z={z:.5f}'.format(x=x, y=y, z=z)
plt.gca().format_coord = fmt
plt.show()
The documentation example shows how you can insert z-value labels into your plot
Script: http://matplotlib.sourceforge.net/mpl_examples/pylab_examples/contour_demo.py
Basically, it's
plt.figure()
CS = plt.contour(X, Y, Z)
plt.clabel(CS, inline=1, fontsize=10)
plt.title('Simplest default with labels')
Just a variant of wilywampa's answer. If you already have a pre-computed grid of interpolated contour values because your data is sparse or if you have a huge data matrix, this might be suitable for you.
import matplotlib.pyplot as plt
import numpy as np
resolution = 100
Z = np.arange(resolution**2).reshape(-1, resolution)
X, Y = np.mgrid[:Z.shape[0], :Z.shape[1]]
cs = plt.contourf(X, Y, Z)
Xflat, Yflat, Zflat = X.flatten(), Y.flatten(), Z.flatten()
def fmt(x, y):
# get closest point with known data
dist = np.linalg.norm(np.vstack([Xflat - x, Yflat - y]), axis=0)
idx = np.argmin(dist)
z = Zflat[idx]
return 'x={x:.5f} y={y:.5f} z={z:.5f}'.format(x=x, y=y, z=z)
plt.colorbar()
plt.gca().format_coord = fmt
plt.show()
Ex:
I have to plot data which is in the following format :
x = range(6)
y = range(11)
and z depends on x, y
For each value of x, there should be a continuous curve that shows the variation of z w.r.t y and the curves for different values of x must be disconnected
I am using mplot3d and it is not very clear how to plot disconnected curves.
This is what it looks like using bar plots.
You could overlay multiple plots using Axes3D.plot:
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as axes3d
import numpy as np
x = np.arange(6)
y = np.linspace(0, 11, 50)
z = x[:, np.newaxis] + y**2
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection = '3d')
for xval, zrow in zip(x, z):
ax.plot(xval*np.ones_like(y), y, zrow, color = 'black')
plt.show()