Saltar al contenido

(python) trazar una superficie 3d con un mapa de colores como cuarta dimensión, función de x, y, z

Solución:

Esta respuesta aborda el problema de la gráfica de superficie 4d. Utiliza matplotlib’s plot_surface función en lugar de plot_trisurf.

Básicamente, desea remodelar sus variables x, y, z en matrices 2d de la misma dimensión. Para agregar la cuarta dimensión como un mapa de colores, debe proporcionar otra matriz 2d de la misma dimensión que sus variables de 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 alterar el mapa de colores a su gusto. Nótese que su valor se adquiere del 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 puede evitarse estableciendo explícitamente los límites del mapa de colores como hice en el ejemplo de código.

Muchas gracias a @Frik por su gran respuesta, me ayudó a lograr una trama similar a la solicitada por el OP.

Sin embargo, descubrí que se pueden hacer algunas simplificaciones al 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()

ingrese la descripción de la imagen aquí

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 puede evitarse estableciendo explícitamente los límites del mapa de colores como hice en el ejemplo de código.

Encontré que esta declaración es incorrecta. De hecho, 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 Falsa, no se realiza ninguna 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 el SO Create own colormap usando matplotlib y trazar la escala de color

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()
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *