grafici di superficie in matplotlib


104

Ho un elenco di 3 tuple che rappresentano un insieme di punti nello spazio 3D. Voglio tracciare una superficie che copra tutti questi punti.

La plot_surfacefunzione nel mplot3dpacchetto richiede che gli argomenti X, Y e Z siano array 2d. Èplot_surface la funzione giusta per tracciare la superficie e come posso trasformare i miei dati nel formato richiesto?

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


Inizia a taggare tutti quei duplicati in superficie e a chiudere i duplicati l'uno nell'altro. Tagga anche numpy , mesh per quelli che riguardano la generazione di meshgrid.
smci

Risposte:


120

Per le superfici è un po 'diverso da un elenco di 3-tuple, dovresti passare una griglia per il dominio negli array 2d.

Se tutto ciò che hai è un elenco di punti 3d, piuttosto che qualche funzione f(x, y) -> z, allora avrai un problema perché ci sono diversi modi per triangolare quella nuvola di punti 3d in una superficie.

Ecco un esempio di superficie liscia:

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
Ciao, grazie per questo. Puoi per favore spiegarci come avere una funzione f(x,y) -> zti dà più informazioni rispetto all'uso di un semplice approccio a liste come inizialmente aveva l'OP.
Gregory Kuhn

16
Ma cosa fai quando z è una variabile indipendente e non una funzione di x e y?
Labibah

4
In questo caso, forse dovresti guardare plot_trisurfinvece. Ma come ho già detto, non è banale perché devi triangolare la superficie e ci sono più soluzioni. Come esempio di base, considera solo i 4 punti dati da (0, 0, 0.2), (0, 1, 0), (1, 1, 0.2), (1, 0, 0). Visto dall'alto, sembra proprio un quadrato con una leggera piega. Ma lungo quale diagonale si verifica la "piega"? È la diagonale "alta" a 0,2 o la diagonale "bassa" a 0? Entrambe sono superfici valide! Quindi è necessario scegliere un algoritmo di triangolazione prima di avere una soluzione ben definita.
wim

Perché da mpl_toolkits.mplot3d importa Axes3D, ma Axes3D non viene utilizzato da nessuna parte nel codice sopra?
絢 瀬 絵 里

5
Questa importazione ha effetti collaterali. L'utilizzo di kwarg projection='3d'nella chiamata fig.add_subplotnon sarà disponibile senza questa importazione.
wim

34

È possibile leggere i dati direttamente da alcuni file e tracciare

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

Se necessario puoi passare vmin e vmax per definire l'intervallo della barra dei colori, ad es

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

superficie

Sezione Bonus

Mi chiedevo come fare alcuni grafici interattivi, in questo caso con dati artificiali

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
in senso stretto, i panda non sono necessari qui.
downer

Ho difficoltà a riprodurre questa trama. Quali sarebbero alcuni valori campione (più piccoli) per ottenere questo risultato?
JRsz

21

Mi sono appena imbattuto in questo stesso problema. Ho uniformemente distanziati i dati in 3 matrici 1-D al posto degli array 2-D che matplotlib's plot_surfacebisogni. I miei dati si trovavano in un, pandas.DataFramequindi ecco l' matplotlib.plot_surfaceesempio con le modifiche per tracciare 3 array 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')

Questo è l'esempio originale. L'aggiunta di questo bit successivo crea lo stesso grafico da 3 array 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()

Ecco le cifre risultanti:

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine


Mi chiedevo se è possibile rimuovere le linee che arrivano sulla superficie (immagine sopra), voglio dire è possibile dare alla superficie un aspetto lucido invece che squamoso? grazie. @ stvn66
diffracteD

@diffracteD, prova a utilizzare una dimensione della griglia più piccola. Sono quasi certo che sia ciò che determina la larghezza tra i contorni. Valutando su una griglia più fine, dovresti essenzialmente diminuire la "dimensione dei pixel" e aumentare la risoluzione, avvicinandoti a un gradiente più uniforme.
Steven C. Howell

C'è un modo per colorare la superficie di cui sopra in base a categorie specifiche? Per es. La categoria x, y, z è il formato dei dati e vorrei colorare la superficie che passa per x, y, z in base a una particolare categoria.
Rudresh Ajgaonkar

@RudreshAjgaonkar, dovresti essere in grado di utilizzare tre comandi di trama separati, uno per ciascuna delle tue categorie, utilizzando qualsiasi colore desideri per ciascuna delle tre.
Steven C. Howell

potete fornire un codice di esempio per favore? sono abbastanza nuovo per matplotlib e python.
Rudresh Ajgaonkar

4

Giusto per intervenire, Emanuel aveva la risposta che io (e probabilmente molti altri) stavo cercando. Se hai dati sparsi in 3D in 3 array separati, i panda sono un aiuto incredibile e funzionano molto meglio delle altre opzioni. Per elaborare, supponiamo che x, y, z siano alcune variabili arbitrarie. Nel mio caso si trattava di c, gamma ed errori perché stavo testando una macchina vettoriale di supporto. Esistono molte scelte potenziali per tracciare i dati:

  • scatter3D (cParams, gammas, avg_errors_array): funziona ma è eccessivamente semplicistico
  • plot_wireframe (cParams, gammas, avg_errors_array): funziona, ma sembrerà brutto se i tuoi dati non sono ordinati bene, come è potenzialmente il caso di enormi blocchi di dati scientifici reali
  • ax.plot3D (cParams, gammas, avg_errors_array) - simile a wireframe

Grafico wireframe dei dati

Grafico wireframe dei dati

Dispersione 3D dei dati

Dispersione 3D dei dati

Il codice ha questo aspetto:

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

Ecco l'output finale:

plot_trisurf dei dati xyz


3

controlla l'esempio ufficiale. X, Y e Z sono effettivamente array 2d, numpy.meshgrid () è un modo semplice per ottenere 2d x, y mesh da 1d xey valori.

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

ecco un modo pitonico per convertire le tue tuple 3 in array 3 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)

Ecco la triangolazione (interpolazione) di mtaplotlib delaunay, converte 1d x, y, z in qualcosa di conforme (?):

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


No ... XYZ sono bidimensionali in questo esempio.
wim

Mi correggo. Usa meshgrid () se i tuoi dati sono spaziati in modo uniforme, come nell'esempio collegato. Interpola ad esempio con griddata () se i tuoi dati non sono spaziati uniformemente.
Dima Tisnek

1

In Matlab Ho fatto qualcosa di simile utilizzando la delaunayfunzione sul x, ycoordinate unica (non il z), poi tracciando con trimesho trisurf, utilizzandoz come l'altezza.

SciPy ha la classe Delaunay , che si basa sulla stessa libreria QHull sottostante che Matlabdelaunay funzione , quindi dovresti ottenere risultati identici.

Da lì, dovrebbero essere poche righe di codice per convertire questo esempio di plottaggio di poligoni 3D in python-matplotlib in ciò che desideri ottenere, in quanto Delaunayti dà la specifica di ogni poligono triangolare.


Vedi questa risposta basata su ax.plot_trisurf(..).
Evgeni Sergeev

1

Solo per aggiungere alcuni ulteriori pensieri che possono aiutare gli altri con problemi di tipo di dominio irregolare. Per una situazione in cui l'utente ha tre vettori / elenchi, x, y, z che rappresentano una soluzione 2D in cui z deve essere tracciato su una griglia rettangolare come superficie, sono applicabili i commenti 'plot_trisurf ()' di ArtifixR. Un esempio simile ma con dominio non rettangolare è:

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

Il codice sopra produce:

Grafico di superficie per problema di griglia non rettangolare

Tuttavia, questo potrebbe non risolvere tutti i problemi, in particolare quando il problema è definito su un dominio irregolare. Inoltre, nel caso in cui il dominio abbia una o più aree concave, la triangolazione delaunay può provocare la generazione di triangoli spuri esterni al dominio. In questi casi, questi triangoli irregolari devono essere rimossi dalla triangolazione per ottenere la corretta rappresentazione della superficie. Per queste situazioni, l'utente potrebbe dover includere esplicitamente il calcolo della triangolazione delaunay in modo che questi triangoli possano essere rimossi a livello di programmazione. In queste circostanze, il codice seguente potrebbe sostituire il codice di trama precedente:


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

Di seguito vengono forniti grafici di esempio che illustrano la soluzione 1) con triangoli spuri e 2) dove sono stati rimossi:

inserisci qui la descrizione dell'immagine

triangoli rimossi

Spero che quanto sopra possa essere di aiuto alle persone con situazioni di concavità nei dati della soluzione.


0

Non è possibile creare direttamente una superficie 3D utilizzando i dati. Ti consiglierei di costruire un modello di interpolazione usando alcuni strumenti come pykridge . Il processo includerà tre passaggi:

  1. Addestra un modello di interpolazione utilizzando pykridge
  2. Costruisci una griglia da Xe Yusandomeshgrid
  3. Interpolare i valori per Z

Dopo aver creato la griglia e i Zvalori corrispondenti , ora sei pronto per iniziare plot_surface. Nota che a seconda della dimensione dei tuoi dati, la meshgridfunzione può essere eseguita per un po '. La soluzione alternativa consiste nel creare campioni con spaziatura uniforme utilizzando gli assi np.linspacefor Xe Y, quindi applicare l'interpolazione per dedurre i Zvalori necessari . In tal caso, i valori interpolati potrebbero essere diversi dall'originale Zperché Xe Ysono cambiati.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.