Solo graficar parte de una figura 3d usando matplotlib

Tuve un problema cuando estaba dibujando una figura en 3D usando matplotlib de python. Usando la siguiente función de Python, obtuve esta figura:

Figura 1

Aquí X , Y son cuadrículas en malla y Z y Z_ son funciones de X e Y C representa el color de la superficie.

 import numpy as np from mpl_toolkits.mplot3d import Axes3D from matplotlib import cm import matplotlib.pyplot as plt def plot(X, Y, Z, Z_, C): fig = plt.figure() ax = fig.gca(projection='3d') surf = ax.plot_surface( X, Y, Z, rstride=1, cstride=1, facecolors=cm.jet(C), linewidth=0, antialiased=False, shade=False) surf_ = ax.plot_surface( X, Y, Z_, rstride=1, cstride=1, facecolors=cm.jet(C), linewidth=0, antialiased=False, shade=False) ax.view_init(elev=7,azim=45) plt.show() 

Pero ahora quiero cortar esta figura horizontalmente y solo la parte cuya z está entre -1 y 2 permanece.

Lo que quiero, trazado con gnuplot, es esto:

Figura 2

He intentado ax.set_zlim3d y ax.set_zlim , pero ninguno de ellos me da la figura deseada. ¿Alguien sabe cómo hacerlo usando python?

Niza intersecciones cónicas que tienes allí 🙂

Lo que intenta hacer debe lograrse configurando los datos Z que desea ignorar en NaN . Usando la estructura de la banda de unión apretada del grafeno como ejemplo:

 import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D # generate dummy data (graphene tight binding band structure) kvec = np.linspace(-np.pi,np.pi,101) kx,ky = np.meshgrid(kvec,kvec) E = np.sqrt(1+4*np.cos(3*kx/2)*np.cos(np.sqrt(3)/2*ky) + 4*np.cos(np.sqrt(3)/2*ky)**2) # plot full dataset fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.plot_surface(kx,ky,E,cmap='viridis',vmin=-E.max(),vmax=E.max(),rstride=1,cstride=1) ax.plot_surface(kx,ky,-E,cmap='viridis',vmin=-E.max(),vmax=E.max(),rstride=1,cstride=1) # focus on Dirac cones Elim = 1 #threshold E[E>Elim] = np.nan fig = plt.figure() ax = fig.add_subplot(111, projection='3d') #ax.plot_surface(kx2,ky2,E2,cmap='viridis',vmin=-Elim,vmax=Elim) #ax.plot_surface(kx2,ky2,-E2,cmap='viridis',vmin=-Elim,vmax=Elim) ax.plot_surface(kx,ky,E,cmap='viridis',rstride=1,cstride=1,vmin=-Elim,vmax=Elim) ax.plot_surface(kx,ky,-E,cmap='viridis',rstride=1,cstride=1,vmin=-Elim,vmax=Elim) plt.show() 

Los resultados se ven así:

estructura de banda completa de grafeno Conos de grafeno Dirac

Desafortunadamente, hay problemas con la representación del segundo caso: el orden de profundidad aparente de los datos se confunde en este último caso: los conos en el fondo se representan frente a los frontales (esto es mucho más claro en una gráfica interactiva) . El problema es que hay más agujeros que los datos reales, y los datos no están conectados, lo que confunde al renderizador de plot_surface . Matplotlib tiene un renderizador 2D, por lo que la visualización en 3D es un poco pirateado. Esto significa que, para superficies superpuestas complejas, la mayoría de las veces obtendrá artefactos de representación (en particular, dos superficies simplemente conectadas están completamente detrás o completamente una frente a la otra).

Podemos evitar el error de renderizado haciendo un poco más de trabajo: mantener los datos en una sola superficie al no usar nan s, pero en cambio colorear la superficie para que sea invisible donde no nos interese. Dado que la superficie que estamos dibujando ahora incluye toda la superficie original, tenemos que configurar el zlim manualmente para enfocarnos en nuestra región de interés. Para el ejemplo anterior:

 from matplotlib.cm import get_cmap # create a color mapping manually Elim = 1 #threshold cmap = get_cmap('viridis') colors_top = cmap((E + Elim)/2/Elim) # listed colormap that maps E from [-Elim, Elim] to [0.0, 1.0] for color mapping colors_bott = cmap((-E + Elim)/2/Elim) # same for -E branch colors_top[E > Elim, -1] = 0 # set outlying faces to be invisible (100% transparent) colors_bott[-E < -Elim, -1] = 0 # in nature you would instead have something like this: #zmin,zmax = -1,1 # where to cut the _single_ input surface (x,y,z) #cmap = get_cmap('viridis') #colors = cmap((z - zmin)/(zmax - zmin)) #colors[(z < zmin) | (z > zmax), -1] = 0 # then plot_surface(x, y, z, facecolors=colors, ...) # or for your specific case where you have X, Y, Z and C: #colors = get_cmap('viridis')(C) #colors[(z < zmin) | (z > zmax), -1] = 0 # then plot_surface(x, y, z, facecolors=colors, ...) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') # pass the mapped colours as the facecolors keyword arg s1 = ax.plot_surface(kx, ky, E, facecolors=colors_top, rstride=1, cstride=1) s2 = ax.plot_surface(kx, ky, -E, facecolors=colors_bott, rstride=1, cstride=1) # but now we need to manually hide the invisible part of the surface: ax.set_zlim(-Elim, Elim) plt.show() 

Aquí está la salida: versión actualizada, sin artefacto de renderizado

Tenga en cuenta que se ve un poco diferente de las cifras anteriores porque han transcurrido 3 años y la versión actual de matplotlib (3.0.2) tiene estilos predeterminados muy diferentes (y mucho más bonitos). En particular, los bordes ahora son transparentes en las plots de superficie. Pero el punto principal es que el error de renderización se ha ido, lo cual es evidente si empiezas a girar la superficie en una ttwig interactiva.