J'ai une liste de 3-tuples représentant un ensemble de points dans l'espace 3D. Je veux tracer une surface qui couvre tous ces points. La fonction plot_surface du paquetage mplot3d nécessite comme arguments X, Y et Z des tableaux 2D. Plot_surface est-il la bonne fonction pour tracer la surface et comment transformer mes données au format requis?
data = [(x1,y1,z1),(x2,y2,z2),.....,(xn,yn,zn)]
Pour les surfaces, c'est un peu différent d'une liste de 3-uplets, vous devriez donner une grille pour le domaine en tableaux 2D.
Si tout ce que vous avez est une liste de points 3D, plutôt qu'une fonction f(x, y) -> z
, vous aurez un problème car il existe plusieurs façons de trianguler ce nuage de points 3D en une surface.
Voici un exemple de surface lisse:
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()
Vous pouvez lire les données directement à partir de certains fichiers et tracer
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
from sys import argv
x,y,z = np.loadtxt('your_file', unpack=True)
fig = plt.figure()
ax = Axes3D(fig)
surf = ax.plot_trisurf(x, y, z, cmap=cm.jet, linewidth=0.1)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.savefig('teste.pdf')
plt.show()
Si nécessaire, vous pouvez passer vmin et vmax pour définir la plage de barres de couleur, par exemple.
surf = ax.plot_trisurf(x, y, z, cmap=cm.jet, linewidth=0.1, vmin=0, vmax=2000)
Je me demandais comment faire des tracés interactifs, dans ce cas avec des données artificielles
from __future__ import print_function
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
from IPython.display import Image
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits import mplot3d
def f(x, y):
return np.sin(np.sqrt(x ** 2 + y ** 2))
def plot(i):
fig = plt.figure()
ax = plt.axes(projection='3d')
theta = 2 * np.pi * np.random.random(1000)
r = i * np.random.random(1000)
x = np.ravel(r * np.sin(theta))
y = np.ravel(r * np.cos(theta))
z = f(x, y)
ax.plot_trisurf(x, y, z, cmap='viridis', edgecolor='none')
fig.tight_layout()
interactive_plot = interactive(plot, i=(2, 10))
interactive_plot
Je viens de rencontrer ce même problème. J'ai des données régulièrement réparties dans des tableaux à 3 dimensions à la place des tableaux à deux dimensions que le plot_surface
de matplotlib
veut. Mes données se trouvaient dans un pandas.DataFrame
donc voici le matplotlib.plot_surface
exemple avec les modifications apportées aux tableaux du graphe 3 1-D du tracé.
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')
C'est l'exemple original. L'ajout de ce bit suivant crée le même tracé à partir de 3 tableaux 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()
Voici les chiffres obtenus:
vérifiez l'exemple officiel. X, Y et Z sont bien des tableaux 2d, numpy.meshgrid () est un moyen simple d’obtenir un maillage 2d x, y à partir de valeurs 1d x et y.
http://matplotlib.sourceforge.net/mpl_examples/mplot3d/surface3d_demo.py
voici comment Pythonic convertit vos 3-tuples en 3 tableaux 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)
Voici la triangulation mtaplotlib delaunay (interpolation), elle convertit 1d x, y, z en quelque chose de conforme (?):
http://matplotlib.sourceforge.net/api/mlab_api.html#matplotlib.mlab.griddata
Emanuel avait la réponse que je cherche (et probablement beaucoup d’autres). Si vous avez des données 3D dispersées dans 3 tableaux distincts, pandas est une aide incroyable et fonctionne beaucoup mieux que les autres options. Pour élaborer, supposons que vos x, y, z soient des variables arbitraires. Dans mon cas, il s’agissait de c, de gamma et d’erreurs car je testais une machine à vecteurs de support. Il existe de nombreux choix potentiels pour tracer les données:
Diagramme filaire des données
Dispersion 3D des données
Le code ressemble à ceci:
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()
Voici le résultat final:
Dans Matlab, j’ai fait quelque chose de similaire en utilisant la fonction delaunay
sur les coordonnées x
, y
(pas le z
), puis en traçant avec trimesh
ou trisurf
, en utilisant z
comme hauteur.
SciPy a la classe Delaunay , qui repose sur la même bibliothèque QHull sous-jacente que la fonction delaunay
de Matlab, vous devriez donc obtenir des résultats identiques.
A partir de là, convertissez cet exemple Représentation graphique de polygones 3D dans python-matplotlib en ce que vous souhaitez obtenir, car Delaunay
vous donne la spécification de chaque polygone triangulaire. .
Il n'est pas possible de créer directement une surface 3D en utilisant vos données. Je vous recommanderais de construire un modèle d'interpolation en utilisant des outils tels que pykridge . Le processus comprendra trois étapes:
pykridge
X
et Y
en utilisant meshgrid
Z
Après avoir créé votre grille et les valeurs Z
correspondantes, vous êtes prêt à utiliser la méthode plot_surface
. Notez que selon la taille de vos données, la fonction meshgrid
peut s'exécuter pendant un certain temps. La solution de contournement consiste à créer des échantillons régulièrement espacés en utilisant les axes np.linspace
pour X
et Y
, puis à appliquer une interpolation pour déduire les valeurs Z
nécessaires. Si tel est le cas, les valeurs interpolées peuvent différer de l'original Z
car X
et Y
ont changé.