web-dev-qa-db-fra.com

parcelles de surface en matplotlib

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)]

86
Graddy

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()

3d

101
wim

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)

surface

Section Bonus

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
21
Emanuel Fontelles

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:

enter image description hereenter image description here

19
Steven C. Howell

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

3
Dima Tisnek

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:

  • scatter3D (cParams, gammas, avg_errors_array) - cela fonctionne mais est trop simpliste
  • plot_wireframe (cParams, gammas, avg_errors_array) - cela fonctionne, mais aura une apparence moche si vos données ne sont pas bien triées, comme c'est potentiellement le cas avec d'énormes quantités de données scientifiques réelles
  • ax.plot3D (cParams, gammas, avg_errors_array) - similaire à une structure filaire

Diagramme filaire des données

Wireframe plot of the data

Dispersion 3D des données

3d scatter of the data

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:

plot_trisurf of xyz data

2
ArtifexR

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. .

1
Evgeni Sergeev

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:

  1. Former un modèle d'interpolation en utilisant pykridge
  2. Construire une grille à partir de X et Y en utilisant meshgrid
  3. Interpole les valeurs pour 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é.

0
lenhhoxung