Oberflächenplots in Matplotlib


104

Ich habe eine Liste von 3 Tupeln, die eine Reihe von Punkten im 3D-Raum darstellen. Ich möchte eine Oberfläche zeichnen, die alle diese Punkte abdeckt.

Die plot_surfaceFunktion im mplot3dPaket erfordert als Argumente X, Y und Z 2d-Arrays. Ist plot_surfacedie richtige Funktion zum Zeichnen der Oberfläche und wie transformiere ich meine Daten in das erforderliche Format?

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


Bitte starten Sie alle diese Duplikate Tagging Oberfläche und Duplikate ineinander zu schließen. Markieren Sie auch numpy , mesh für diejenigen, bei denen es um die Generierung von Meshgrids geht.
smci

Antworten:


120

Bei Oberflächen unterscheidet es sich ein wenig von einer Liste mit 3 Tupeln. Sie sollten ein Raster für die Domäne in 2D-Arrays übergeben.

Wenn Sie nur eine Liste von 3D-Punkten anstelle einer Funktion haben f(x, y) -> z, haben Sie ein Problem, da es mehrere Möglichkeiten gibt, diese 3D-Punktwolke in eine Oberfläche zu triangulieren.

Hier ist ein Beispiel für eine glatte Oberfläche:

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


1
Hallo, danke dafür. Können Sie bitte f(x,y) -> znäher erläutern, wie Sie mit einer Funktion mehr Informationen erhalten, als nur einen Listenansatz zu verwenden, wie er das OP ursprünglich hatte?
Gregory Kuhn

15
Aber was machen Sie, wenn z eine unabhängige Variable ist und keine Funktion von x und y?
Labibah

4
In diesem Fall sollten Sie sich vielleicht plot_trisurfstattdessen umsehen. Aber wie ich bereits erwähnt habe, ist es nicht trivial, da Sie die Oberfläche triangulieren müssen und es mehrere Lösungen gibt. Betrachten Sie als grundlegendes Beispiel nur die 4 Punkte, die durch (0, 0, 0,2), (0, 1, 0), (1, 1, 0,2), (1, 0, 0) gegeben sind. Von oben gesehen sieht es aus wie ein Quadrat mit einer leichten Falte. Aber entlang welcher Diagonale tritt die "Falte" auf? Ist es die "hohe" Diagonale bei 0,2 oder die "niedrige" Diagonale bei 0? Beides sind gültige Oberflächen! Sie müssen also einen Triangulationsalgorithmus auswählen, bevor Sie eine genau definierte Lösung finden.
wim

Warum aus mpl_toolkits.mplot3d Axes3D importieren, Axes3D wird jedoch nirgendwo im obigen Code verwendet?
瀬 絵 里

5
Dieser Import hat Nebenwirkungen. Die Verwendung von kwarg projection='3d'im Aufruf fig.add_subplotist ohne diesen Import nicht verfügbar.
wim

32

Sie können Daten direkt aus einer Datei und einem Plot lesen

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

Bei Bedarf können Sie vmin und vmax übergeben, um den Farbbalkenbereich zu definieren, z

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

Oberfläche

Bonusbereich

Ich habe mich gefragt, wie ich einige interaktive Diagramme erstellen soll, in diesem Fall mit künstlichen Daten

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

5
Genau genommen sind Pandas hier nicht notwendig.
Wermutstropfen

Es fällt mir schwer, diese Handlung zu reproduzieren. Was wären einige (kleinere) Stichprobenwerte, um dies zu erreichen?
JRsz

21

Ich bin gerade auf dasselbe Problem gestoßen. Ich habe Daten gleichmäßig beabstandet , daß anstelle der 2-D - Arrays , die in 3-D - Arrays 1 ist matplotlib‚s plot_surfacemöchte. Meine Daten befanden sich zufällig in einem. pandas.DataFrameHier ist das matplotlib.plot_surfaceBeispiel mit den Änderungen zum Zeichnen von 3 1-D-Arrays.

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

Das ist das ursprüngliche Beispiel. Durch Hinzufügen dieses nächsten Bits wird das gleiche Diagramm aus 3 1-D-Arrays erstellt.

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

Hier sind die resultierenden Zahlen:

Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein


Ich habe mich gefragt, ob es möglich ist, die Linien auf der Oberfläche zu entfernen (Bild oben). Ich meine, ist es möglich, der Oberfläche ein glänzendes Aussehen zu verleihen, anstatt ein schuppiges Aussehen zu erhalten? Danke. @ stvn66
diffracteD

@diffracteD, versuchen Sie es mit einer kleineren Rastergröße. Ich bin mir fast sicher, dass dies die Breite zwischen den Konturen bestimmt. Wenn Sie in einem feineren Raster auswerten, sollten Sie im Wesentlichen die "Pixelgröße" verringern und die Auflösung erhöhen, um sich einem gleichmäßigeren Gradienten zu nähern.
Steven C. Howell

Gibt es eine Möglichkeit, die obige Oberfläche nach bestimmten Kategorien zu färben? Zum Beispiel. Kategorie x, y, z ist das Datenformat und ich möchte die Oberfläche, die durch x, y, z verläuft, entsprechend einer bestimmten Kategorie färben.
Rudresh Ajgaonkar

@RudreshAjgaonkar, Sie sollten in der Lage sein, drei separate Plotbefehle zu verwenden, einen für jede Ihrer Kategorien, wobei Sie für jede der drei die gewünschte Farbe verwenden.
Steven C. Howell

Können Sie bitte einen Beispielcode angeben? Ich bin ziemlich neu in Matplotlib und Python.
Rudresh Ajgaonkar

4

Emanuel hatte die Antwort, nach der ich (und wahrscheinlich viele andere) suchen. Wenn Sie 3D-Streudaten in 3 separaten Arrays haben, ist Pandas eine unglaubliche Hilfe und funktioniert viel besser als die anderen Optionen. Angenommen, Ihre x, y, z sind beliebige Variablen. In meinem Fall waren dies c, Gamma und Fehler, da ich eine Support-Vektor-Maschine getestet habe. Es gibt viele Möglichkeiten, die Daten zu zeichnen:

  • Scatter3D (cParams, Gammas, avg_errors_array) - dies funktioniert, ist aber zu simpel
  • plot_wireframe (cParams, gammas, avg_errors_array) - dies funktioniert, sieht aber hässlich aus, wenn Ihre Daten nicht gut sortiert sind, wie dies möglicherweise bei großen Teilen realer wissenschaftlicher Daten der Fall ist
  • ax.plot3D (cParams, gammas, avg_errors_array) - ähnlich wie bei wireframe

Drahtgitterdiagramm der Daten

Drahtgitterdiagramm der Daten

3D-Streuung der Daten

3D-Streuung der Daten

Der Code sieht folgendermaßen aus:

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

Hier ist die endgültige Ausgabe:

plot_trisurf von xyz-Daten


3

Überprüfen Sie das offizielle Beispiel. X, Y und Z sind in der Tat 2d-Arrays. Numpy.meshgrid () ist eine einfache Methode, um 2d x, y-Maschen aus 1d x- und y-Werten zu erhalten.

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

Hier ist eine pythonische Methode, um Ihre 3-Tupel in 3 1d-Arrays zu konvertieren.

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)

Hier ist mtaplotlib delaunay Triangulation (Interpolation), es konvertiert 1d x, y, z in etwas Konformes (?):

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


Nein ... XYZ sind in diesem Beispiel zweidimensional.
wim

Ich stehe korrigiert. Verwenden Sie meshgrid (), wenn Ihre Daten einen einheitlichen Abstand haben, wie im verknüpften Beispiel. Interpolieren Sie z. B. mit griddata (), wenn Ihre Daten nicht gleichmäßig verteilt sind.
Dima Tisnek

1

In Matlab Ich habe etwas ähnliches unter Verwendung der delaunayFunktion auf dem x, ycoords nur (nicht z), dann Plotten mit trimeshoder trisurfunter Verwendung zder Höhe.

SciPy verfügt über die Delaunay- Klasse, die auf derselben zugrunde liegenden QHull-Bibliothek basiert wie die Matlab- delaunayFunktion. Daher sollten Sie identische Ergebnisse erhalten.

Von dort aus sollten es einige Codezeilen sein, um dieses 3D-Plotten von Polygonen in Python-Matplotlib- Beispiel in das zu konvertieren, was Sie erreichen möchten, da DelaunaySie die Spezifikation jedes dreieckigen Polygons erhalten.


Siehe diese Antwort basierend auf ax.plot_trisurf(..).
Evgeni Sergeev

1

Nur um einige weitere Gedanken hinzuzufügen, die anderen bei Problemen mit unregelmäßigen Domänentypen helfen können. Für eine Situation, in der der Benutzer drei Vektoren / Listen hat, x, y, z, die eine 2D-Lösung darstellen, bei der z als Oberfläche auf einem rechteckigen Gitter dargestellt werden soll, gelten die Kommentare von 'plot_trisurf ()' von ArtifixR. Ein ähnliches Beispiel, jedoch mit nicht rechteckiger Domäne, ist:

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

# problem parameters
nu = 50; nv = 50
u = np.linspace(0, 2*np.pi, nu,) 
v = np.linspace(0, np.pi, nv,)

xx = np.zeros((nu,nv),dtype='d')
yy = np.zeros((nu,nv),dtype='d')
zz = np.zeros((nu,nv),dtype='d')

# populate x,y,z arrays
for i in range(nu):
  for j in range(nv):
    xx[i,j] = np.sin(v[j])*np.cos(u[i])
    yy[i,j] = np.sin(v[j])*np.sin(u[i])
    zz[i,j] = np.exp(-4*(xx[i,j]**2 + yy[i,j]**2)) # bell curve

# convert arrays to vectors
x = xx.flatten()
y = yy.flatten()
z = zz.flatten()

# Plot solution surface
fig = plt.figure(figsize=(6,6))
ax = Axes3D(fig)
ax.plot_trisurf(x, y, z, cmap=cm.jet, linewidth=0,
                antialiased=False)
ax.set_title(r'trisurf example',fontsize=16, color='k')
ax.view_init(60, 35)
fig.tight_layout()
plt.show()

Der obige Code erzeugt:

Flächendiagramm für nicht rechteckiges Gitterproblem

Dies löst jedoch möglicherweise nicht alle Probleme, insbesondere wenn das Problem in einer unregelmäßigen Domäne definiert ist. In dem Fall, in dem die Domäne einen oder mehrere konkave Bereiche aufweist, kann die Delaunay-Triangulation dazu führen, dass außerhalb der Domäne falsche Dreiecke erzeugt werden. In solchen Fällen müssen diese Schurkendreiecke aus der Triangulation entfernt werden, um die korrekte Oberflächendarstellung zu erreichen. In diesen Situationen muss der Benutzer möglicherweise die Delaunay-Triangulationsberechnung explizit einbeziehen, damit diese Dreiecke programmgesteuert entfernt werden können. Unter diesen Umständen könnte der folgende Code den vorherigen Plotcode ersetzen:


import matplotlib.tri as mtri 
import scipy.spatial
# plot final solution
pts = np.vstack([x, y]).T
tess = scipy.spatial.Delaunay(pts) # tessilation

# Create the matplotlib Triangulation object
xx = tess.points[:, 0]
yy = tess.points[:, 1]
tri = tess.vertices # or tess.simplices depending on scipy version

#############################################################
# NOTE: If 2D domain has concave properties one has to
#       remove delaunay triangles that are exterior to the domain.
#       This operation is problem specific!
#       For simple situations create a polygon of the
#       domain from boundary nodes and identify triangles
#       in 'tri' outside the polygon. Then delete them from
#       'tri'.
#       <ADD THE CODE HERE>
#############################################################

triDat = mtri.Triangulation(x=pts[:, 0], y=pts[:, 1], triangles=tri)

# Plot solution surface
fig = plt.figure(figsize=(6,6))
ax = fig.gca(projection='3d')
ax.plot_trisurf(triDat, z, linewidth=0, edgecolor='none',
                antialiased=False, cmap=cm.jet)
ax.set_title(r'trisurf with delaunay triangulation', 
          fontsize=16, color='k')
plt.show()

Die nachstehenden Beispieldiagramme veranschaulichen die Lösung 1) mit falschen Dreiecken und 2) wo sie entfernt wurden:

Geben Sie hier die Bildbeschreibung ein

Dreiecke entfernt

Ich hoffe, dass das oben Genannte für Personen mit Konkavitätssituationen in den Lösungsdaten hilfreich sein kann.


0

Es ist nicht möglich, mit Ihren Daten direkt eine 3D-Oberfläche zu erstellen. Ich würde Ihnen empfehlen, ein Interpolationsmodell mit einigen Tools wie pykridge zu erstellen . Der Prozess umfasst drei Schritte:

  1. Trainieren Sie ein Interpolationsmodell mit pykridge
  2. Erstellen Sie ein Raster aus Xund Yverwenden Siemeshgrid
  3. Werte für interpolieren Z

Nachdem Sie Ihr Raster und die entsprechenden ZWerte erstellt haben, können Sie loslegen plot_surface. Beachten Sie, dass die meshgridFunktion abhängig von der Größe Ihrer Daten eine Weile ausgeführt werden kann. Die Problemumgehung besteht darin, mit np.linspacefor Xund YAchsen gleichmäßig verteilte Stichproben zu erstellen und dann eine Interpolation anzuwenden, um auf die erforderlichen ZWerte zu schließen . Wenn ja, von der ursprünglichen die interpolierten Werte unterschiedlich könnten , Zweil Xund Yhaben sich geändert.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.