Python por ejemplo
Tugurium/Python

Python, por ejemplo

Gráficos 3D

1.1.7 Gráficos 3D

Originalmente matplotlib se diseñó para la generación de gráficos bidimensionales, pero en sucesivas versiones se han ido incluyendo capacidades para la generación de gráficos tridimensionales. Esta capacidad ayuda a visualizar más información, haciéndola además más atractiva.

Para generar gráficos 3D en matplotlib necesitamos crear una instancia de ejes de la clase Axes3D, empleando el argumento de palabra clave projection='3d' en cualquiera de las funciones de creación de ejes add_axes() o add_subplot().

Antes de matplotlib 3.2, era necesario importar explícitamente el módulo mpl_toolkits.mplot3d para hacer gráficos 3D. En sucesivas versiones basta con el submódulo pyplot.

Todos los ejemplos se pueden manipular con el ratón para verlos desde distintos ángulos, es una funcionalidad de matplotlib con los gráficos en 3D. Para ello basta con copiar el código y ejecutarlo desde el IDLE.

En el siguiente código creamos unos ejes para un gráfico 3D con el parámetro clave projection='3d'. Esto basta para crear la figura de los ejes 3D vacíos.

Vamos titular el gráfico y disponer etiquetas para ver los ejes X, Y, Z.

matplotlib_06_01_3d.py
import matplotlib.pyplot as plt


# crear trazado en tres dimensiones
ax = plt.axes(projection='3d')

# etiquetar los ejes
ax.set_xlabel("Eje X", color='red')
ax.set_ylabel("Eje Y", color='red')
ax.set_zlabel("Eje Z", color='red')

# establecer título 
ax.set_title("Ejes en 3D", color='red')

# mostar el gráfico
plt.show()

El resultado lo vemos en la siguiente imagen.

Ejes para representaciones 3D

Con los ejes 3D activados ya podemos trazar diversos gráficos tridimensionales. Como el ángulo de visión por defecto no siempre permite apreciar la figura, tenemos la posibilidad de girar los ejes, estableciendo la elevación, el acimut y el giro de los ejes en grados (no en radianes) utilizando el método view_init(), que tiene la firma:

Axes3D.view_init(elev=None, azim=None, roll=None, vertical_axis='z')
Parámetros

elev: Ángulo de elevación en grados. Rota la cámara por encima del plano atravesado por el eje vertical predeterminado Z, la elevación define el ángulo de la ubicación de la cámara por encima del plano XY. Por defecto None.

azim: Ángulo azimutal en grados. Gira la cámara alrededor del eje vertical, con un ángulo positivo correspondiente a una rotación a derechas. Por defecto None.

roll: Ángulo de giro en grados. Rota la cámara alrededor del eje de visión. Un ángulo positivo gira la cámara en el sentido de las agujas del reloj, haciendo que la escena gire en sentido contrario. Por defecto None

vertical_axis: Eje a alinear verticalmente. Azim rota alrededor de este eje. Tiene los valores {'z', 'x', 'y'}. Por defecto 'z'.

Procedemos a crear una grupo de ejes a los que aplicaremos diversos giros. En todos ellos señalaremos el origen de coordenadas (000), como referencia para observar los giros.

matplotlib_06_01_3d_1.py
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec


# crear trazado en tres dimensiones
fig = plt.figure(figsize=(8,5))
gs = gridspec.GridSpec(nrows=2, ncols=3, figure=fig)
ax1 = fig.add_subplot(gs[0, 0], projection='3d')
ax2 = fig.add_subplot(gs[0, 1], projection='3d')
ax3 = fig.add_subplot(gs[0, 2], projection='3d')
ax4 = fig.add_subplot(gs[1, 0], projection='3d')
ax5 = fig.add_subplot(gs[1, 1], projection='3d')
ax6 = fig.add_subplot(gs[1, 2], projection='3d')

# etiquetar los ejes
for ax in fig.get_axes():
    ax.set_xticklabels([])
    ax.set_yticklabels([])
    ax.set_zticklabels([])
    ax.text(0,0,0,'000')

# ajustar la posición de la cámara
ax2.view_init(5, None)
ax3.view_init(75, None)
ax4.view_init(25, None, 25)
ax5.view_init(None, 5)
ax6.view_init(None, 75)

# establecer títulos
ax1.set_title("Por defecto", color='red')
ax2.set_title("(5, None)", color='red')
ax3.set_title("(75, None)", color='red')
ax4.set_title("(25, None, 25)", color='red', y=-0.1)
ax5.set_title("(None, 5)", color='red', y=-0.1)
ax6.set_title("(None, 75)", color='red', y=-0.1)

# mostar el gráfico
plt.show()

El resultado nos muestralas distintas posiciones de los ejes 3D según los giros aplicados.

Distintas posiciones de los ejes 3D

A continuación mostraremos varios tipos de gráficos tridimensionales que nos facilita matplotlib para representar la información de la manera más adecuada a nuestros datos.

1.1.7.1 Gráficos de líneas

Para crear gráficos de líneas en un espacio tridimensional haremos uso de la función plot(), que tiene la firma:

plot(xs, ys, zs, zdir='z', **kwargs)
Parámetros

xs: Vector con las coordenadas del eje X para cada punto.

ys: Vector con las coordenadas del eje Y para cada punto.

zs: Coordenadas Z de los vértices en forma de variable o vector; una para todos los puntos o una para cada punto.

zdir: Cuando se trazan datos 2D, la dirección a usar como z. {'x', 'y', 'z'}. Por defecto 'z'.

Para el siguiente ejemplo generamos una secuencia de vértices para los ejes X, Y, Z, con las funciones seno y coseno.

Creamos una figura vacía con los ejes del gráfico indicando que será una proyección 3D.

Después, pasamos las coordenadas X, Y, Z a la función plot() para trazar el gráfico (también podemos usar plot3D()). Trazaremos las líneas en color rojo.

matplotlib_06_02_3d_lineas.py
import matplotlib.pyplot as plt
import math


# relación de valores a visualizar
grados = [i for i in range (-360, 361, 10)]
seno = [math.sin(math.radians(i)) for i in grados]
coseno = [math.cos(math.radians(i)) for i in grados]

# crear trazado en tres dimensiones
ax = plt.axes(projection='3d')

# dibujar los valores x, y, z
ax.plot(grados, seno, coseno, 'red')

# mostar el gráfico
plt.show()

El resultado lo vemos en el gráfico siguiente.

Gráfico de líneas 3D

1.1.7.2 Gráficos de barras

Los gráficos de barras se utilizan para realizar comparativas entre diferentes series de valores. La extensión que ofrece la profundidad en el gráfico tridimensional amplía la capacidad de representación de información comparativa.

En un gráfico de barras en 3D necesitamos una posición y un tamaño que tenemos con los ejes X, Y, Z. Las posiciones X e Y representarán las coordenadas de la barra a través del plano (x, y), mientras que la altura de las barras será proporcional al valor a visualizar representado por el eje Z.

Para trazar un gráfico de barras tridimensional haremos uso de la función bar3d(), que tiene la firma:

bar3d(x, y, z, dx, dy, dz, color=None, zsort='average', shade=True, lightsource=None, *args, data=None, **kwargs)
Parámetros

x, y, z: Coordenadas del punto de anclaje de las barras. X es el eje horizontal, Y es el eje de profundidad y Z es el eje vertical.

dx, dy, dz: Datos de anchura, profundidad y altura de las barras.

color: Color de las barras global o individual. Puede ser un único color, para colorear todas las barras del mismo color; o una matriz de colores de longitud el número de barras, para colorear cada barra independientemente.

Cuando se generan barras múltiples, x, y, z tienen que ser vectores. dx, dy, dz pueden ser vectores o escalares.

Vamos a prepara un ejemplo en el que visualizaremos un gráfico de barras para presentar la producción de trigo, en millones de toneladas, de cinco países en un periodo de cuatro años. La información sobre producción la tenemos en un matriz de 4x5.

Decidimos presentar los países en el eje X, los años en el eje Y, con los valores de producción en el Z.

Primero vamos a preparar los valores de las coordenadas para las posiciones de las barras (x, y, z).

El valor de x será un vector con las posiciones de cinco barras, espaciadas a lo largo del eje X, que se repiten cuatro veces de acuerdo al número de años que tenemos para representar.

El valor de y será un vector con las posiciones espaciadas para las barras a lo largo del eje Y.

El valor de z es un vector inicializado a cero para el total de barras, ya que haremos que todas las barras partan del valor cero en el eje Z.

El siguiente paso es crear la información para la anchura, profundidad y altura de las barras (dx, dy, dz).

Para dx y dy creamos un vector con un valor de espaciado entre las barras, mientras que para dz vamos a utilizar los valores de producción. Como tenemos que pasar un vector a la función debemos de aplanar la matriz de producción original.

Para darle un poco de alegría al gráfico establecemos un vector con los colores para las barras.

Creamos una figura vacía con los ejes del gráfico indicando que será una proyección 3D.

Después, pasamos todos los vectores que hemos creado con los valores para x, y, z, dx, dy, dz y colores a la función bar3d().

Por último, para ilustrar un poco el gráfico, añadimos marcas y etiquetas en los ejes y terminamos poniendo un título al gráfico.

matplotlib_06_03_3d_bar.py
import matplotlib.pyplot as plt


# relación de valores a visualizar
# producción de trigo en millones de toneladas
paises = ['China', 'India', 'Rusia', 'EEUU', 'Canadá']
annum = [2019, 2020, 2021, 2022]
produccion =[[133.6, 103.59, 74.45, 52.58, 32.66],
             [134.25, 107.59, 85.89, 49.69, 35.18],
             [131.44, 99.70, 72.14, 51.29, 31.77],
             [138.00, 103.00, 91.00, 44.90, 33.82]] 

# coordenadas de las barras 
x = [1.5, 2.5, 3.5, 4.5, 5.5]*4
y = [0.5, 0.5, 0.5, 0.5, 0.5, 1.5, 1.5, 1.5, 1.5, 1.5,
     2.5, 2.5, 2.5, 2.5, 2.5, 3.5, 3.5, 3.5, 3.5, 3.5]
z = [0 for i in range(20)]

# anchura, profundidad y altura de las barras 
dx = [0.5 for i in range(20)]
dy = [0.5 for i in range(20)]
# aplanamos la matriz de producción
dz = [num for vector in produccion for num in vector]

# establecer colores
colores = ['r','g','b', 'm', 'y'] * 4

# crear trazado en tres dimensiones
ax = plt.axes(projection = "3d")

# dibujar los valores
ax.bar3d(x, y, z, dx, dy, dz, color=colores, alpha=0.8)

# establecer marcas en los ejes
ax.set_xticks([1,2,3,4,5])
ax.set_xticklabels(paises)
ax.set_yticks([1,2,3,4])
ax.set_yticklabels(annum)
 
# establecer etiquetas en los ejes
ax.set_xlabel('Paises', color='r')
ax.set_ylabel('Años', color='r')
ax.set_zlabel('Millones toneladas', color='r')

# establecer título 
ax.set_title('Producción de trigo', color='r')

# mostar el gráfico
plt.show()

El resultado, como poco, es llamativo.

Gráfico de barras 3D

1.1.7.3 Gráficos de dispersión

Un gráfico de dispersión muestra la correlación entre las variables como una colección de puntos en un eje cartesiano. En el caso de los gráficos de dispersión tridimensionales disponemos de tres variables para los ejes X, Y, Z.

El gráfico de dispersión 3D se crea utilizando la función scatter3D(), que tiene la firma:

scatter(xs, ys, zs=0, zdir='z', s=20, c=None, depthshade=True, *args, data=None, **kwargs)
Parámetros

xs: Vector con las coordenadas del eje X para cada punto.

ys: Vector con las coordenadas del eje Y para cada punto.

zs: Vector con las coordenadas del eje Z para cada punto. O un único valor para colocar todos los puntos en el mismo plano.

zdir: La dirección del eje para los valores de zs. Esto es aplicable cuando se trazan datos 2D en un eje 3D. Los datos deben pasarse como xs, ys. Configurando zdir como 'y' los datos se trazan en el plano X-Z. Los posibles valores son: {'x', 'y', 'z', '-x', '-y', '-z'}. Por defecto 'z'.

s: Tamaño de las marcas en puntos**2. Puede ser una matriz de la misma longitud que xs e ys o un único valor para que todas las marcas tengan el mismo tamaño. Por defecto 20.

c: Color o secuencia de colores.

Para nuestro ejemplo vamos a generar una secuencia de números para el eje X. Para obtener los de los ejes Y y Z, barajaremos los valores del eje X.

Generaremos de esta manera dos series que mostraremos en el mismo gráfico con la función scatter3D().

matplotlib_06_04_3d_scatter.py
import matplotlib.pyplot as plt
import random


# relación de valores a visualizar
x = [i for i in range(-10, 20)]
y = random.sample(x, k=30)
z = random.sample(y, k=30)

x2 = [i for i in range(-20, 10)]
y2 = random.sample(x2, k=30)
z2 = random.sample(y2, k=30)

# crear trazado en tres dimensiones
ax = plt.axes(projection='3d')

# dibujar los valores
ax.scatter3D(x, y, z, c='g', marker='o')
ax.scatter3D(x2, y2, z2, c ='r', marker='*')

# mostar el gráfico
plt.show()

El resultado nos muestra los dos conjuntos de puntos, cada uno con el color asignado.

Gráfico de dispersión 3D

1.1.7.4 Gráficos de malla

El gráfico de malla es el esqueleto de un gráfico de superficie. Representa los datos mediante líneas, con huecos entre ellas.

En un gráfico bidimensional partimos de todos los pares de valores de los vectores X e Y. Para el gráfico tridimensional necesitamos un tercer vector Z en función de X e Y.

El gráfico de malla 3D se crea utilizando la función plot_wireframe(), que tiene la firma:

plot_wireframe(X, Y, Z, **kwargs)
Parámetros

X, Y, Z: Vectores con las coordenadas de los vértices.

Los siguientes parámetros opcionales nos facilitan el ajuste del gráfico.

rcount, ccount: Número máximo de muestras utilizadas en cada dirección. Si hay más datos de entrada, se recortarán al número indicado. Si se establece un contador a cero, los datos no se muestrean en la dirección correspondiente, lo que producirá un gráfico lineal 3D en lugar de un gráfico de malla. Por defecto 50.

rstride, cstride: Reducción del muestreo en cada dirección. Estos argumentos se excluyen mutuamente con rcount y ccount. Si sólo se define uno de los argumentos el otro se establece por defecto en 1. Si se establece un stride en cero, los datos no se muestrearán en la dirección correspondiente, lo que producirá un gráfico lineal 3D en lugar de un gráfico de malla.

El modo general de trazado utiliza por defecto rstride=cstride=1 en lugar del nuevo valor por defecto rcount=ccount=50.

En el ejemplo vamos a hacer uso de la función get_test_data() que genera datos de prueba, para ello debemos importar la clase axes3d() del módulo mpl_toolkits.mplot3d, que amablemente nos proporciona esta facilidad.

Una vez tengamos los vectores para X, Y, Z, creamos una figura vacía con los ejes del gráfico indicando que será una proyección 3D.

Llamaremos dos veces a la función plot_wireframe(), con distintos valores en los parámetros opcionales, y distinto color, para observar cómo se crea la malla en cada caso.

matplotlib_06_05_3d_malla.py
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d


# obtener datos a trazar
X, Y, Z = axes3d.get_test_data(0.053)

# crear trazado en tres dimensiones
ax = plt.axes(projection='3d')

# dibujar los valores
ax.plot_wireframe(X, Y, Z, rstride=7, cstride=7)
ax.plot_wireframe(X, Y, Z, rcount=3, ccount=3, color='r')

# mostar el gráfico
plt.show()

En el gráfico resultante tenemos las dos mallas que hemos dibujado, haciendo uso de más o menos puntos.

Gráfico de malla

1.1.7.5 Gráficos de superficie

La generación de gráficos de superficie es similar a la de gráficos de malla en cuanto a la creación de los valores para X, Y, Z. Mientras que los gráficos de malla conectan puntos de datos mediante líneas, los gráficos de superficie rellenan el espacio entre los puntos con una superficie continua, que proporciona una representación más suave de los datos, especialmente cuando se tiene un conjunto de datos más grande y denso.

En el caso de gráficos de superficie hacemos uso de la función plot_surface(), que tiene la firma:

plot_surface(X, Y, Z, *, norm=None, vmin=None, vmax=None, lightsource=None, **kwargs)
Parámetros

X, Y, Z: Vectores con las coordenadas de los vértices.

Los siguientes parámetros opcionales nos facilitan el ajuste del gráfico.

rcount, ccount: Número máximo de muestras utilizadas en cada dirección. Si hay más datos de entrada, se recortarán al número indicado. Si se establece un contador a cero, los datos no se muestrearán en la dirección correspondiente, lo que producirá un gráfico lineal 3D en lugar de un gráfico de malla. Por defecto 50.

rstride, cstride: Reducción del muestreo en cada dirección. Estos argumentos se excluyen mutuamente con rcount y ccount. Si sólo se define uno de los argumentos, el otro se establece por defecto en 1. Si se establece un stride en cero, los datos no se muestrearán en la dirección correspondiente, lo que producirá un gráfico lineal 3D en lugar de un gráfico de malla.

El modo general de trazado utiliza por defecto rstride=cstride=1 en lugar del nuevo valor por defecto rcount=ccount=50.

color: Color de los parches de superficie. Por defecto se colorea en un color sólido.

cmap: Mapa de colores de los polígonos de la superficie.

La elección entre gráficos de malla y gráficos de superficie dependerá de los datos y de cómo deseemos visualizarlos. De hecho, para el siguiente gráfico de superficie vamos a emplear los mismos datos que para el gráfico de malla visto anteriormente, dejando que extrapole el resto de puntos para la superficie.

matplotlib_06_06_3d_superficie.py
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d


# obtener datos a trazar
X, Y, Z = axes3d.get_test_data(0.053)

# crear trazado en tres dimensiones
ax = plt.axes(projection='3d')

# dibujar los valores
ax.plot_surface(X, Y, Z, rstride=7, cstride=7)

# mostar el gráfico
plt.show()

El resultado lo vemos en distintas tonalidades del color por defecto.

Gráfico de superficie monocromo

Como hemos comentado, es posible añadir un mapa de colores para que coloree la superficie, lo que va a dar más profundidad a la imagen, aportando más información visual.

Vamos a ampliar el ejemplo coloreando el gráfico e incluyendo una barra con la escala de colores para los valores del gráfico. Para ello basta con añadir un mapa de colores en la función plot_surface() con el parámetro opcional cmap.

La barra con la escala de colores la añadimos a la figura con la función colorbar(), aplicada sobre la superficie que nos devuelve la función plot_surface().

matplotlib_06_07_3d_superficie.py
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d


# obtener datos a trazar
X, Y, Z = axes3d.get_test_data(0.053)

# crear trazado en tres dimensiones
fig = plt.figure()
ax = plt.axes(projection='3d')

# dibujar los valores
# coloreando la superficie
surf = ax.plot_surface(X, Y, Z, rstride=7, cstride=7, cmap='plasma')

# añadir una escala de colores
fig.colorbar(surf, shrink=0.7)

# mostar el gráfico
plt.show()

El nuevo gráfico ofrece una imagen con la misma información, pero visualmente más descriptiva.

Gráfico de superficie coloreado

Gráficos 3D

    • Gráficos 3D
      • Gráficos de líneas
      • Gráficos de barras
      • Gráficos de dispersión
      • Gráficos de malla
      • Gráficos de superficie