Te recomendamos que pruebes esta respuesta en un ambiente controlado antes de enviarlo a producción, un saludo.
Solución:
Esta respuesta aborda el problema de la gráfica de superficie 4d. Utiliza matplotlib plot_surface
función en lugar de plot_trisurf
.
Básicamente, desea remodelar sus variables x, y y z en matrices 2d de la misma dimensión. Para agregar la cuarta dimensión como un mapa de colores, debe proporcionar otro 2d array de la misma dimensión que las variables de sus ejes.
A continuación se muestra un código de ejemplo para un gráfico 3D con el mapa de colores correspondiente a los valores de x. los facecolors
El argumento se utiliza para modificar el mapa de colores a su gusto. Tenga en cuenta que su valor se adquiere de la to_rgba()
función en el matplotlib.cm.ScalarMappable
clase.
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
# domains
x = np.logspace(-1.,np.log10(5),50) # [0.1, 5]
y = np.linspace(6,9,50) # [6, 9]
z = np.linspace(-1,1,50) # [-1, 1]
# convert to 2d matrices
Z = np.outer(z.T, z) # 50x50
X, Y = np.meshgrid(x, y) # 50x50
# fourth dimention - colormap
# create colormap according to x-value (can use any 50x50 array)
color_dimension = X # change to desired fourth dimension
minn, maxx = color_dimension.min(), color_dimension.max()
norm = matplotlib.colors.Normalize(minn, maxx)
m = plt.cm.ScalarMappable(norm=norm, cmap='jet')
m.set_array([])
fcolors = m.to_rgba(color_dimension)
# plot
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(X,Y,Z, rstride=1, cstride=1, facecolors=fcolors, vmin=minn, vmax=maxx, shade=False)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
fig.canvas.show()
La respuesta a la que hice referencia (y otras) menciona que debe normalizar sus datos de cuarta dimensión. Parece que esto se puede evitar estableciendo explícitamente los límites del mapa de colores como lo hice en el ejemplo de código.
Muchas gracias a @Frik por su excelente respuesta, me ayudó a lograr una trama similar a la solicitada por el OP.
Sin embargo, descubrí que se pueden hacer algunas simplificaciones en el código y que podrían ser de interés. Fragmento y figura a continuación.
import matplotlib.pyplot as plt
# This import registers the 3D projection, but is otherwise unused.
from mpl_toolkits.mplot3d import Axes3D # noqa: F401 unused import
from mpl_toolkits.mplot3d.axes3d import get_test_data
import numpy as np
fig, ax = plt.subplots(subplot_kw='projection': '3d')
X, Y, Z = get_test_data(0.05)
C = np.linspace(-5, 5, Z.size).reshape(Z.shape)
scamap = plt.cm.ScalarMappable(cmap='inferno')
fcolors = scamap.to_rgba(C)
ax.plot_surface(X, Y, Z, facecolors=fcolors, cmap='inferno')
fig.colorbar(scamap)
plt.show()
Finalmente, también quería comentar lo que escribió @Frik:
La respuesta a la que hice referencia (y otras) menciona que debe normalizar sus datos de cuarta dimensión. Parece que esto se puede evitar estableciendo explícitamente los límites del mapa de colores como lo hice en el ejemplo de código.
Encontré que esta declaración es incorrecta. En efecto, si uno echa un vistazo a to_rgba
se puede ver que hay un norm
palabra clave que está configurada de forma predeterminada en True
. Aquí es exactamente donde ocurre la normalización. También se incluye la siguiente declaración:
Si la norma es Falso, no se realiza la normalización de los datos de entrada y se supone que está en el rango (0-1).
De hecho, desea que sus datos se encuentren en (0-1).
Este código se basa en la demostración de trisurf http://matplotlib.org/examples/mplot3d/trisurf3d_demo.html
Agregué una función make_colormap() basada en SO Create own colormap usando matplotlib y plot color scale
También se agregó una secuencia w=tan(-x*y) que genera un mapa de color basado en esa función, en la escala de grises.
Puedes jugar con la construcción del cdict para agregarle más colores, pero creo que la escala de grises es una buena prueba de concepto…
Lo siento, no pude trabajar directamente con su ejemplo, debido a la falta de un código de trabajo mínimo.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.colors as mcolors
###################
def make_colormap(seq):
"""Return a LinearSegmentedColormap
seq: a sequence of floats and RGB-tuples. The floats should be increasing
and in the interval (0,1).
"""
#%
cdict = 'red': [], 'green': [], 'blue': []
# make a lin_space with the number of records from seq.
x = np.linspace(0,1, len(seq))
#%
for i in range(len(seq)):
segment = x[i]
tone = seq[i]
cdict['red'].append([segment, tone, tone])
cdict['green'].append([segment, tone, tone])
cdict['blue'].append([segment, tone, tone])
#%
return mcolors.LinearSegmentedColormap('CustomMap', cdict)
#############################
n_angles = 36
n_radii = 8
# An array of radii
# Does not include radius r=0, this is to eliminate duplicate points
radii = np.linspace(0.125, 1.0, 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 = np.sin(-x*y)
w = np.tan(-x*y)
colors = make_colormap(w)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_trisurf(x, y, z, cmap=colors, linewidth=0.2)
plt.show()
Si te sientes a gusto, eres capaz de dejar un tutorial acerca de qué le añadirías a este post.