plots de superficie en matplotlib

Tengo una lista de 3 tuplas que representan un conjunto de puntos en el espacio 3D. Quiero trazar una superficie que cubra todos estos puntos. La función plot_surface en el paquete mplot3d requiere como argumentos X, Y y Z, que son matrices 2d. ¿Plot_surface es la función correcta para trazar la superficie y cómo transformo mis datos al formato requerido?

data = [(x1,y1,z1),(x2,y2,z2),.....,(xn,yn,zn)]

Para las superficies es un poco diferente a una lista de 3 tuplas, debe pasar una cuadrícula para el dominio en matrices 2d.

Si todo lo que tiene es una lista de puntos 3d, en lugar de alguna función f(x, y) -> z , entonces tendrá un problema porque hay varias formas de triangular esa nube de puntos 3d en una superficie.

Aquí hay un ejemplo de superficie lisa:

 import numpy as np from mpl_toolkits.mplot3d import Axes3D # Axes3D import has side effects, it enables using projection='3d' in add_subplot import matplotlib.pyplot as plt import random def fun(x, y): return x**2 + y fig = plt.figure() ax = fig.add_subplot(111, projection='3d') x = y = np.arange(-3.0, 3.0, 0.05) X, Y = np.meshgrid(x, y) zs = np.array(fun(np.ravel(X), np.ravel(Y))) Z = zs.reshape(X.shape) ax.plot_surface(X, Y, Z) ax.set_xlabel('X Label') ax.set_ylabel('Y Label') ax.set_zlabel('Z Label') plt.show() 

3d

Acabo de encontrar este mismo problema. He espaciado uniformemente los datos que están en 3 arreglos 1-D en lugar de los arreglos 2-D que quiere plot_surface . Mis datos se encontraban en un pandas.DataFrame así que aquí está el ejemplo matplotlib.plot_surface con las modificaciones para trazar 3 matrices 1-D.

 from mpl_toolkits.mplot3d import Axes3D from matplotlib import cm from matplotlib.ticker import LinearLocator, FormatStrFormatter import matplotlib.pyplot as plt import numpy as np X = np.arange(-5, 5, 0.25) Y = np.arange(-5, 5, 0.25) X, Y = np.meshgrid(X, Y) R = np.sqrt(X**2 + Y**2) Z = np.sin(R) fig = plt.figure() ax = fig.gca(projection='3d') surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False) ax.set_zlim(-1.01, 1.01) ax.zaxis.set_major_locator(LinearLocator(10)) ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f')) fig.colorbar(surf, shrink=0.5, aspect=5) plt.title('Original Code') 

Ese es el ejemplo original. Agregar este bit siguiente en crea la misma ttwig de 3 arreglos 1-D.

 # ~~~~ MODIFICATION TO EXAMPLE BEGINS HERE ~~~~ # import pandas as pd from scipy.interpolate import griddata # create 1D-arrays from the 2D-arrays x = X.reshape(1600) y = Y.reshape(1600) z = Z.reshape(1600) xyz = {'x': x, 'y': y, 'z': z} # put the data into a pandas DataFrame (this is what my data looks like) df = pd.DataFrame(xyz, index=range(len(xyz['x']))) # re-create the 2D-arrays x1 = np.linspace(df['x'].min(), df['x'].max(), len(df['x'].unique())) y1 = np.linspace(df['y'].min(), df['y'].max(), len(df['y'].unique())) x2, y2 = np.meshgrid(x1, y1) z2 = griddata((df['x'], df['y']), df['z'], (x2, y2), method='cubic') fig = plt.figure() ax = fig.gca(projection='3d') surf = ax.plot_surface(x2, y2, z2, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False) ax.set_zlim(-1.01, 1.01) ax.zaxis.set_major_locator(LinearLocator(10)) ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f')) fig.colorbar(surf, shrink=0.5, aspect=5) plt.title('Meshgrid Created from 3 1D Arrays') # ~~~~ MODIFICATION TO EXAMPLE ENDS HERE ~~~~ # plt.show() 

Aquí están las figuras resultantes:

introduzca la descripción de la imagen aquíintroduzca la descripción de la imagen aquí

Hago esto con algunas líneas en python usando PANDAS, ¡la ttwig es hermosa!

 from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt from matplotlib import cm import numpy as np import pandas as pd from sys import argv file = argv[1] x,y,z = np.loadtxt(file, unpack=True) df = pd.DataFrame({'x': x, 'y': y, 'z': z}) fig = plt.figure() ax = Axes3D(fig) surf = ax.plot_trisurf(df.x, df.y, df.z, cmap=cm.jet, linewidth=0.1) fig.colorbar(surf, shrink=0.5, aspect=5) plt.savefig('teste.pdf') plt.show() 

Si es necesario, puede pasar vmin y vmax para definir el rango de la barra de colores, por ejemplo

 surf = ax.plot_trisurf(df.x, df.y, df.z, cmap=cm.jet, linewidth=0.1, vmin=0, vmax=2000) 

superficie

Revisa el ejemplo oficial. X, Y y Z son de hecho matrices 2d, numpy.meshgrid () es una forma sencilla de obtener 2d x, y malla de los valores 1d x y y.

http://matplotlib.sourceforge.net/mpl_examples/mplot3d/surface3d_demo.py

Esta es la forma en Pythonic para convertir tus 3-tuplas en arreglos de 3 1d.

 data = [(1,2,3), (10,20,30), (11, 22, 33), (110, 220, 330)] X,Y,Z = zip(*data) In [7]: X Out[7]: (1, 10, 11, 110) In [8]: Y Out[8]: (2, 20, 22, 220) In [9]: Z Out[9]: (3, 30, 33, 330) 

Aquí está la triangulación delaunay mtaplotlib (interpolación), que convierte 1d x, y, z en algo compatible (?):

http://matplotlib.sourceforge.net/api/mlab_api.html#matplotlib.mlab.griddata

En Matlab hice algo similar usando la función delaunay en las trimesh x , y solamente (no en la z ), luego trimesh con trimesh o trisurf , usando z como la altura.

SciPy tiene la clase Delaunay , que se basa en la misma biblioteca QHull subyacente que la función delaunay de Matlab, por lo que debería obtener resultados idénticos.

A partir de ahí, deberían ser unas pocas líneas de código para convertir este Ejemplo de Polígonos 3D en python-matplotlib en lo que deseas lograr, ya que Delaunay te da la especificación de cada polígono triangular.

Solo para intervenir, Emanuel tuvo la respuesta que yo (y probablemente muchos otros) estamos buscando. Si tiene datos 3D dispersos en 3 arreglos separados, los pandas son una ayuda increíble y funcionan mucho mejor que las otras opciones. Para elaborar, suponga que su x, y, z son algunas variables arbitrarias. En mi caso, fueron c, gamma y errores porque estaba probando una máquina de vectores de soporte. Hay muchas opciones potenciales para trazar los datos:

  • scatter3D (cParams, gammas, avg_errors_array) – esto funciona pero es demasiado simplista
  • plot_wireframe (cParams, gammas, avg_errors_array) – esto funciona, pero se verá feo si sus datos no están bien ordenados, como es potencialmente el caso de fragmentos masivos de datos científicos reales
  • ax.plot3D (cParams, gammas, avg_errors_array) – similar a wireframe

Esquema alámbrico de los datos

Esquema alámbrico de los datos

Dispersión 3D de los datos.

Dispersión 3D de los datos.

El código se ve así:

  fig = plt.figure() ax = fig.gca(projection='3d') ax.set_xlabel('c parameter') ax.set_ylabel('gamma parameter') ax.set_zlabel('Error rate') #ax.plot_wireframe(cParams, gammas, avg_errors_array) #ax.plot3D(cParams, gammas, avg_errors_array) #ax.scatter3D(cParams, gammas, avg_errors_array, zdir='z',cmap='viridis') df = pd.DataFrame({'x': cParams, 'y': gammas, 'z': avg_errors_array}) surf = ax.plot_trisurf(df.x, df.y, df.z, cmap=cm.jet, linewidth=0.1) fig.colorbar(surf, shrink=0.5, aspect=5) plt.savefig('./plots/avgErrs_vs_C_andgamma_type_%s.png'%(k)) plt.show() 

Aquí está la salida final:

plot_trisurf de datos xyz

No es posible hacer directamente una superficie 3D utilizando sus datos. Te recomendaría construir un modelo de interpolación usando algunas herramientas como pykridge . El proceso incluirá tres pasos:

  1. Entrena un modelo de interpolación usando pykridge
  2. Construye una grilla de X e Y usando meshgrid
  3. Interpolar valores para Z

Una vez que haya creado su cuadrícula y los valores Z correspondientes, ahora está listo para plot_surface con plot_surface . Tenga en cuenta que, dependiendo del tamaño de sus datos, la función meshgrid puede ejecutarse por un tiempo. La solución consiste en crear muestras espaciadas uniformemente utilizando np.linspace para los ejes X e Y , luego aplicar interpolación para inferir los valores Z necesarios. Si es así, los valores interpolados podrían ser diferentes de los Z originales porque X e Y han cambiado.