3D plots using maplot3d from matplotlib- - python

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()

Related

3D plot in python plot (X, Y, Z, data)

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?

3D surface graph with matplotlib using dataframe columns to input the data

I have a spreadsheet file that I would like to input to create a 3D surface graph using Matplotlib in Python.
I used plot_trisurf and it worked, but I need the projections of the contour profiles onto the graph that I can get with the surface function, like this example.
I'm struggling to arrange my Z data in a 2D array that I can use to input in the plot_surface method. I tried a lot of things, but none seems to work.
Here it is what I have working, using plot_trisurf
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import pandas as pd
df=pd.read_excel ("/Users/carolethais/Desktop/Dissertação Carol/Códigos/Resultados/res_02_0.5.xlsx")
fig = plt.figure()
ax = fig.gca(projection='3d')
# I got the graph using trisurf
graf=ax.plot_trisurf(df["Diametro"],df["Comprimento"], df["temp_out"], cmap=matplotlib.cm.coolwarm)
ax.set_xlim(0, 0.5)
ax.set_ylim(0, 100)
ax.set_zlim(25,40)
fig.colorbar(graf, shrink=0.5, aspect=15)
ax.set_xlabel('Diâmetro (m)')
ax.set_ylabel('Comprimento (m)')
ax.set_zlabel('Temperatura de Saída (ºC)')
plt.show()
This is a part of my df, dataframe:
Diametro Comprimento temp_out
0 0.334294 0.787092 34.801994
1 0.334294 8.187065 32.465551
2 0.334294 26.155976 29.206090
3 0.334294 43.648591 27.792126
4 0.334294 60.768219 27.163233
... ... ... ...
59995 0.437266 14.113660 31.947302
59996 0.437266 25.208851 30.317583
59997 0.437266 33.823035 29.405461
59998 0.437266 57.724209 27.891616
59999 0.437266 62.455890 27.709298
I tried this approach to use the imported data with plot_surface, but what I got was indeed a graph but it didn't work, here it's the way the graph looked with this approach:
Thank you so much
A different approach, based on re-gridding the data, that doesn't require that the original data is specified on a regular grid [deeply inspired by this example;-].
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.tri as tri
from mpl_toolkits.mplot3d import Axes3D
np.random.seed(19880808)
# compute the sombrero over a cloud of random points
npts = 10000
x, y = np.random.uniform(-5, 5, npts), np.random.uniform(-5, 5, npts)
z = np.cos(1.5*np.sqrt(x*x + y*y))/(1+0.33*(x*x+y*y))
# prepare the interpolator
triang = tri.Triangulation(x, y)
interpolator = tri.LinearTriInterpolator(triang, z)
# do the interpolation
xi = yi = np.linspace(-5, 5, 101)
Xi, Yi = np.meshgrid(xi, yi)
Zi = interpolator(Xi, Yi)
# plotting
fig = plt.figure()
ax = fig.gca(projection='3d')
norm = plt.Normalize(-1,1)
ax.plot_surface(Xi, Yi, Zi,
cmap='inferno',
norm=plt.Normalize(-1,1))
plt.show()
plot_trisurf expects x, y, z as 1D arrays while plot_surface expects X, Y, Z as 2D arrays or as x, y, Z with x, y being 1D array and Z a 2D array.
Your data consists of 3 1D arrays, so plotting them with plot_trisurf is immediate but you need to use plot_surface to be able to project the isolines on the coordinate planes... You need to reshape your data.
It seems that you have 60000 data points, in the following I assume that you have a regular grid 300 points in the x direction and 200 points in y — but what is important is the idea of regular grid.
The code below shows
the use of plot_trisurf (with a coarser mesh), similar to your code;
the correct use of reshaping and its application in plot_surface;
note that the number of rows in reshaping corresponds to the number
of points in y and the number of columns to the number of points in x;
and 4. incorrect use of reshaping, the resulting subplots are somehow
similar to the plot you showed, maybe you just need to fix the number
of row and columns.
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
x, y = np.arange(30)/3.-5, np.arange(20)/2.-5
x, y = (arr.flatten() for arr in np.meshgrid(x, y))
z = np.cos(1.5*np.sqrt(x*x + y*y))/(1+0.1*(x*x+y*y))
fig, axes = plt.subplots(2, 2, subplot_kw={"projection" : "3d"})
axes = iter(axes.flatten())
ax = next(axes)
ax.plot_trisurf(x,y,z, cmap='Reds')
ax.set_title('Trisurf')
X, Y, Z = (arr.reshape(20,30) for arr in (x,y,z))
ax = next(axes)
ax.plot_surface(X,Y,Z, cmap='Reds')
ax.set_title('Surface 20×30')
X, Y, Z = (arr.reshape(30,20) for arr in (x,y,z))
ax = next(axes)
ax.plot_surface(X,Y,Z, cmap='Reds')
ax.set_title('Surface 30×20')
X, Y, Z = (arr.reshape(40,15) for arr in (x,y,z))
ax = next(axes)
ax.plot_surface(X,Y,Z, cmap='Reds')
ax.set_title('Surface 40×15')
plt.tight_layout()
plt.show()

Matplotlib plot contourf on 3d surface

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()

Boolean masking of arrays in 3D surface plot destroys colormap

I try to 3D-plot function fun and use colormap to show the level of function values. I'd like to plot this function on a non-sqaured area and hence I used boolean mask to set np.nan to certain values in meshgrid. But I got
RuntimeWarning: invalid value encountered in less
cbook._putmask(xa, xa < 0.0, -1)
whenever I added boolean mask. It seems the bug is due to that np.nan cannot be compared in colormap. But I can't find a way to fix this.
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
%matplotlib inline
fun = lambda x: np.sin(x[0])*np.exp(1-np.cos(x[1]))**2 + np.cos(x[1])*np.exp(1-np.sin(x[0]))**2 + (x[0]-x[1])**2
fig = plt.figure(figsize=(8, 5))
ax = fig.gca(projection='3d')
x = np.arange(-6, 6, 3e-2)
y = np.arange(-6, 6, 3e-2)
# A constraint on x and y
x, y = np.meshgrid(x, y)
r2 = (x+5)**2 + (y+5)**2
scope = r2 < 25
# Mask is the cause of the problem
x[scope] = np.nan
y[scope] = np.nan
z = fun(np.array([x, y]))
surf=ax.plot_surface(x, y, z, cmap=cm.jet)
ax.contourf(x, y, z, offset=-120, cmap=cm.jet)
fig.colorbar(surf)
ax.view_init(elev=30, azim=60)
You cannot fix the runtime warning. It's a warning based on the fact that there are nan values in the array.
In order to still get a colorcoded surface plot, you can however use a matplotlib.colors.Normalize instance to tell the surface plot which colors to use.
See full code below:
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
import matplotlib.colors
fun = lambda x: np.sin(x[0])*np.exp(1-np.cos(x[1]))**2 + np.cos(x[1])*np.exp(1-np.sin(x[0]))**2 + (x[0]-x[1])**2
fig = plt.figure(figsize=(8, 5))
ax = fig.gca(projection='3d')
x = np.arange(-6, 6, 3e-2)
y = np.arange(-6, 6, 3e-2)
# A constraint on x and y
x, y = np.meshgrid(x, y)
r2 = (x+5)**2 + (y+5)**2
scope = r2 < 25
# Mask is the cause of the problem
x[scope] = np.nan
y[scope] = np.nan
z = fun(np.array([x, y]))
norm = matplotlib.colors.Normalize(vmin=-120, vmax=120)
cm.jet.set_under((0,0,0,0))
ax.contourf(x, y, z, offset=-120, cmap=cm.jet, norm=norm)
surf=ax.plot_surface(x, y, z, cmap=cm.jet, norm=norm)
fig.colorbar(surf)
#ax.view_init(elev=30, azim=60)
plt.show()

Contour graph in python

How would I make a countour grid in python using matplotlib.pyplot, where the grid is one colour where the z variable is below zero and another when z is equal to or larger than zero? I'm not very familiar with matplotlib so if anyone can give me a simple way of doing this, that would be great.
So far I have:
x= np.arange(0,361)
y= np.arange(0,91)
X,Y = np.meshgrid(x,y)
area = funcarea(L,D,H,W,X,Y) #L,D,H and W are all constants defined elsewhere.
plt.figure()
plt.contourf(X,Y,area)
plt.show()
You can do this using the levels keyword in contourf.
import numpy as np
import matplotlib.pyplot as plt
fig, axs = plt.subplots(1,2)
x = np.linspace(0, 1, 100)
X, Y = np.meshgrid(x, x)
Z = np.sin(X)*np.sin(Y)
levels = np.linspace(-1, 1, 40)
zdata = np.sin(8*X)*np.sin(8*Y)
cs = axs[0].contourf(X, Y, zdata, levels=levels)
fig.colorbar(cs, ax=axs[0], format="%.2f")
cs = axs[1].contourf(X, Y, zdata, levels=[-1,0,1])
fig.colorbar(cs, ax=axs[1])
plt.show()
You can change the colors by choosing and different colormap; using vmin, vmax; etc.

Categories

Resources