oppervlakteplots in matplotlib

Ik heb een lijst met 3 tupels die een reeks punten in de 3D-ruimte vertegenwoordigen. Ik wil een oppervlak plotten dat al deze punten omvat.

De functie plot_surfacein het pakket mplot3dvereist dat als argumenten X,Y en Z 2d-arrays zijn. Is plot_surfacede juiste functie om het oppervlak te plotten en hoe zet ik mijn gegevens om in het vereiste formaat?

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

Antwoord 1, autoriteit 100%

Voor oppervlakken is het een beetje anders dan een lijst van 3-tupels, je moet een raster voor het domein in 2D-arrays doorgeven.

Als je alleen een lijst met 3D-punten hebt, in plaats van een functie f(x, y) -> z, dan heb je een probleem omdat er meerdere manieren zijn om die 3D-puntenwolk in een oppervlak te trianguleren.

Hier is een voorbeeld van een glad oppervlak:

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


Antwoord 2, autoriteit 33%

Je kunt gegevens rechtstreeks uit een bestand lezen en plotten

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

Indien nodig kunt u vmin en vmax doorgeven om het kleurenbalkbereik te definiëren, bijvoorbeeld

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

Bonussectie

Ik vroeg me af hoe ik interactieve plots moest maken, in dit geval met kunstmatige gegevens

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

Antwoord 3, autoriteit 16%

Ik kwam net hetzelfde probleem tegen. Ik heb gelijkmatig verdeelde gegevens in 3 1-D-arrays in plaats van de 2-D-arrays die matplotlib‘s plot_surfacewil. Mijn gegevens bevonden zich toevallig in een pandas.DataFrame, dus hier is de matplotlib.plot_surfacevoorbeeldmet de aanpassingen om 3 1-D arrays te plotten.

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

Dat is het originele voorbeeld. Door dit volgende bit toe te voegen, ontstaat dezelfde plot van 3 1-D-arrays.

# ~~~~ 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 zijn de resulterende cijfers:



4, Autoriteit 4%

Gewoon om te bellen, Emanuel had het antwoord dat ik (en waarschijnlijk veel anderen) op zoek zijn. Als u 3D-verspreide gegevens in 3 afzonderlijke arrays hebt, is Panda’s een ongelooflijke hulp en werkt u veel beter dan de andere opties. Om uit te werken, neem dan dat je X, Y, Z enkele willekeurige variabelen zijn. In mijn geval waren dit C, gamma en fouten omdat ik een ondersteuningsvector-machine testte. Er zijn veel potentiële keuzes om de gegevens te plotten:

  • Scatter3D (CPARAMS, GAMMAS, AVG_ERRORS_ARRAY) – Dit werkt maar is overdreven simplistisch
  • PLOT_WireFrame (CPARAMS, GAMMAS, AVG_ERRORS_ARRAY) – Dit werkt, maar ziet er lelijk uit als uw gegevens niet mooi zijn gesorteerd, zoals potentieel het geval is met enorme brokken echte wetenschappelijke gegevens
  • AX.PLOT3D (CPARAMS, GAMMAS, AVG_ERRORS_ARRAY) – vergelijkbaar met Wireframe

Wireframe-plot van de gegevens

3D-scatter van de gegevens

De code ziet er als volgt uit:

   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 is de laatste uitvoer:


5, Autoriteit 4%

Om enkele verdere gedachten toe te voegen die anderen kunnen helpen met problemen met onregelmatige domeintype. Voor een situatie waarin de gebruiker drie vectoren / lijsten heeft, X, Y, Z die een 2D-oplossing vertegenwoordigt waarbij Z moet worden uitgezet op een rechthoekig raster als een oppervlak, zijn de opmerkingen ‘PLOT_TRISUUR () door ARTIFIXR van toepassing. Een soortgelijk voorbeeld, maar met niet-rechthoekig domein is:

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

De bovenstaande code levert:

Dit lost echter mogelijk niet alle problemen op, vooral niet wanneer het probleem op een onregelmatig domein is gedefinieerd. Ook in het geval dat het domein een of meer concave gebieden heeft, kan de delaunay-triangulatie resulteren in het genereren van onechte driehoeken buiten het domein. In dergelijke gevallen moeten deze malafide driehoeken uit de triangulatie worden verwijderd om de juiste oppervlakteweergave te verkrijgen. Voor deze situaties kan het zijn dat de gebruiker de berekening van de delaunay-triangulatie expliciet moet opnemen, zodat deze driehoeken programmatisch kunnen worden verwijderd. Onder deze omstandigheden zou de volgende code de vorige plotcode kunnen vervangen:


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

Hieronder worden voorbeeldplots gegeven die oplossing 1) illustreren met onechte driehoeken, en 2) waar ze zijn verwijderd:

Ik hoop dat het bovenstaande mensen kan helpen met concave situaties in de oplossingsgegevens.


Antwoord 6, autoriteit 3%

bekijk het officiële voorbeeld.
X,Y en Z zijn inderdaad 2d-arrays, numpy.meshgrid() is een eenvoudige manier om 2d x,y mesh uit 1d x- en y-waarden te halen.

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

hier is een pythonische manier om je 3-tupels om te zetten in 3 1d-arrays.

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 is MTAPLOTLIB DELAUNAY-triangulatie (interpolatie), het converteert 1D X, Y, Z naar iets compliant (?):

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


7

In Matlab heb ik iets soortgelijks gedaan met behulp van de delaunayFunctie op de x, yCoords Alleen (niet de z), DAN SPOTTING MET trimeshOF trisurf, met zALS DE HOOGJE.

Scipy heeft de Delaunay Klasse, die is gebaseerd op dezelfde onderliggende Qhull-bibliotheek die de Matlab’s delaunay-functie is, dus u moet identieke resultaten behalen.

Vanaf daar, het zou een paar regels code moeten zijn om deze plotten 3D polygonen te converteren In Python-Matplotlib Voorbeeld in wat u wilt bereiken, als delaunaygeeft u de specificatie van elke driehoekige polygoon.

Other episodes