157 votes

tracés de surface dans matplotlib

J'ai une liste de 3-tuples représentant un ensemble de points dans un espace 3D. Je veux tracer une surface qui couvre tous ces points.

El plot_surface dans le mplot3d requiert comme arguments X,Y et Z des tableaux de 2d. Est plot_surface la bonne fonction pour tracer la surface et comment transformer mes données dans le format requis ?

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

0 votes

S'il vous plaît, commencez à étiqueter tous ces doublons surface et de fermer les doublons les uns dans les autres. Étiquette également numpy , maille pour celles qui concernent la génération de grilles de maillage.

180voto

wim Points 35274

Pour les surfaces, c'est un peu différent d'une liste de 3-tuples, vous devez passer dans une grille pour le domaine dans des 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 alors 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

4 votes

Bonjour, merci pour cela. Pouvez-vous nous expliquer comment une fonction f(x,y) -> z vous permet d'obtenir plus d'informations qu'en utilisant simplement une approche par liste comme le PO l'a fait initialement.

40 votes

Mais que faites-vous lorsque z est une variable indépendante et non une fonction de x et y ?

9 votes

Dans ce cas, vous devriez peut-être chercher à plot_trisurf à la place. Mais comme je l'ai mentionné, ce n'est pas trivial car il faut trianguler la surface et il existe de multiples solutions. Comme exemple de base, considérons juste les 4 points donnés par (0, 0, 0.2), (0, 1, 0), (1, 1, 0.2), (1, 0, 0). Vu d'en haut, cela ressemble simplement à un carré avec un léger pli. Mais le long de quelle diagonale le "pli" se produit-il ? Est-ce la diagonale "haute" à 0,2 ou la diagonale "basse" à 0 ? Les deux sont des surfaces valables ! Vous devez donc choisir un algorithme de triangulation avant d'avoir une solution bien définie.

59voto

Emanuel Fontelles Points 395

Vous pouvez lire les données directement à partir d'un fichier et les 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 la barre 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 graphiques 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

6 votes

À proprement parler, pandas n'est pas nécessaire ici.

1 votes

J'ai du mal à reproduire ce graphique. Quelles seraient les valeurs d'échantillon (plus petites) pour y parvenir ?

29voto

Steven C. Howell Points 3893

Je viens de rencontrer le même problème. J'ai des données uniformément espacées qui se trouvent dans 3 tableaux 1-D au lieu des tableaux 2-D que j'ai utilisés. matplotlib 's plot_surface veut. Mes données se trouvaient dans un pandas.DataFrame voici donc le matplotlib.plot_surface exemple avec les modifications pour tracer 3 tableaux 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')

C'est l'exemple original. En ajoutant l'élément suivant, on obtient 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 qui en résultent :

enter image description hereenter image description here

0 votes

Je me demandais s'il est possible d'enlever les lignes qui apparaissent sur la surface (l'image ci-dessus), je veux dire qu'il est possible de donner à la surface un aspect brillant au lieu d'un aspect écailleux ? merci. @stvn66

0 votes

@diffracteD, essayez d'utiliser une taille de grille plus petite. Je suis presque certain que c'est ce qui détermine la largeur entre les contours. En évaluant sur une grille plus fine, vous devriez essentiellement diminuer la "taille du pixel" et augmenter la résolution, pour obtenir un gradient plus lisse.

0 votes

Existe-t-il un moyen de colorer la surface ci-dessus en fonction de catégories spécifiques ? Par ex. Catégorie x, y, z est le format des données et je voudrais colorer la surface passant par x,y,z selon une catégorie particulière.

8voto

ArtifexR Points 346

Juste pour intervenir, Emanuel a eu la réponse que je cherchais (et probablement beaucoup d'autres). Si vous avez des données éparpillées en 3d dans 3 tableaux séparés, pandas est d'une aide incroyable et fonctionne bien mieux que les autres options. Pour élaborer, supposons que vos x, y, z sont des variables arbitraires. Dans mon cas, il s'agissait de c, gamma et 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 sera laid si vos données ne sont pas bien triées, comme c'est potentiellement le cas avec de gros volumes de données scientifiques réelles.
  • ax.plot3D(cParams, gammas, avg_errors_array) - similaire à wireframe

Tracé filaire des données

Wireframe plot of the data

Diffusion en 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

8voto

Deep Points 264

Il ne s'agit pas d'une solution générale, mais elle pourrait aider beaucoup de ceux qui ont tapé "matplotlib surface plot" dans Google et ont atterri ici.

Supposons que vous ayez data = [(x1,y1,z1),(x2,y2,z2),.....,(xn,yn,zn)] alors vous pouvez obtenir trois listes 1-d en utilisant x, y, z = zip(*data) . Maintenant, vous pouvez bien sûr créer un nuage de points 3d en utilisant trois listes 1-d.

Mais, pourquoi ne pas en général ces données peuvent-elles être utilisées pour créer un tracé de surface ? Pour comprendre cela, considérez un graphique 3-d vide :

Maintenant, supposons que pour chaque valeur possible de (x, y) sur une grille régulière "discrète", vous ayez une valeur z, alors il n'y a pas de problème et vous pouvez en fait obtenir un tracé de surface :

import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm

x = np.linspace(0, 10, 6)  # [0, 2,..,10] : 6 distinct values
y = np.linspace(0, 20, 5)  # [0, 5,..,20] : 5 distinct values
z = np.linspace(0, 100, 30)  # 6 * 5 = 30 values, 1 for each possible combination of (x,y)

X, Y = np.meshgrid(x, y)
Z = np.reshape(z, X.shape)  # Z.shape must be equal to X.shape = Y.shape

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

ax.plot_surface(X, Y, Z)

ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()

Que se passe-t-il lorsque vous n'avez pas obtenu z pour toutes les combinaisons possibles de (x, y) ? Alors, au point (à l'intersection des deux lignes noires sur le plan x-y du graphique ci-dessus), nous ne savons pas quelle est la valeur de z. Cela peut être n'importe quoi, nous ne savons pas à quel point notre surface devrait être "haute" ou "basse" à ce point (bien qu'elle puisse être approximée en utilisant d'autres fonctions), surface_plot exige que vous lui fournissiez des arguments où X.shape = Y.shape = Z.shape).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X