2012-02-07 21 views
63

Mam listę 3-tek reprezentujących zbiór punktów w przestrzeni 3D. Chcę wykreślić powierzchnię obejmującą wszystkie te punkty. Funkcja plot_surface w pakiecie mplot3d wymaga jako argumentów X, Y i Z, które są tablicami 2d. Czy funkcja działki to właściwa funkcja do wykreślania powierzchni i jak mogę przekształcić moje dane do wymaganego formatu?powierzchnie działek w matplotlib

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

+2

Oto niektóre związane/podobne/zdublowane posty: http://stackoverflow.com/q/3012783/3585557, http://stackoverflow.com/q/12423601/3585557 http : //stackoverflow.com/q/21161884/3585557, http://stackoverflow.com/q/26074542/3585557, http://stackoverflow.com/q/28389606/3585557, http://stackoverflow.com/q/29547687/3585557. –

Odpowiedz

70

Na powierzchni jest nieco inna niż lista 3-krotki, należy przekazać w siatce dla domeny w 2D tablic.

Jeśli wszystko, co masz, to lista punktów 3d, a nie jakaś funkcja f(x, y) -> z, to będziesz miał problem, ponieważ istnieje wiele sposobów na triangulację tej chmury punktów 3D na powierzchnię.

Oto przykład gładka powierzchnia:

import numpy as np 
from mpl_toolkits.mplot3d import Axes3D 
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(x,y) for x,y in zip(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() 

enter image description here

+0

Cześć, dziękuję za to. Czy możesz wyjaśnić, w jaki sposób funkcja 'f (x, y) -> z' dostarcza ci więcej informacji niż po prostu używając metody listy, takiej jak OP, którą początkowo miał. –

+4

Ale co zrobić, gdy z jest zmienną niezależną, a nie funkcją x i y? – Labibah

+2

W takim przypadku powinieneś raczej spojrzeć na 'plot_trisurf'. Ale jak już wspomniałem, nie jest to trywialne, ponieważ trzeba triangulować powierzchnię i istnieje wiele rozwiązań. Jako podstawowy przykład rozważ tylko 4 punkty podane przez (0, 0, 0.2), (0, 1, 0), (1, 1, 0.2), (1, 0, 0). Oglądany z góry wygląda jak kwadrat z lekkim zagięciem. * Ale wzdłuż której przekątnej występuje "fałdowanie"? * Czy jest to "wysoka" przekątna na 0,2 lub "niska" przekątna na 0? Obie są poprawnymi powierzchniami! Musisz więc wybrać algorytm triangulacji, zanim będziesz miał dobrze zdefiniowane rozwiązanie. – wim

4

check oficjalna przykładem. X, Y i Z są rzeczywiście tablicami 2d, numpy.meshgrid() to prosty sposób na uzyskanie siatki 2d x, y z wartości 1 xi y.

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

oto pythonic sposób do konwersji 3-krotki do 3 1d tablic.

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) 

Oto triangulacji mtaplotlib Delaunay (interpolacja), konwertuje 1D X, Y, Z w coś zgodnego (?):

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

+0

Nie ... X Y Z są dwuwymiarowe w tym przykładzie. – wim

+0

Stoję poprawione. Użyj siatki meshgrid(), jeśli twoje dane są równomiernie rozmieszczone, jak w połączonym przykładzie. Interpolować, np. z griddata(), jeśli twoje dane nie są równomiernie rozmieszczone. –

1

W Matlab Zrobiłem coś podobnego przy użyciu funkcji delaunay na tylko współrzędne (a nie z), a następnie wykreślenie za pomocą trimesh lub trisurf, używając jako wzrostu wysokości.

SciPy ma klasę Delaunay, która jest oparta na tej samej podstawowej bibliotece QHull, którą pełni funkcja Matlaba delaunay, więc powinieneś uzyskać identyczne wyniki.

Z tego miejsca powinno być kilka linii kodu, aby przekonwertować ten przykład Plotting 3D Polygons in python-matplotlib na to, co chcesz osiągnąć, ponieważ Delaunay podaje specyfikację każdego trójkątnego wielokąta.

+0

Zobacz [tę odpowiedź] (http://stackoverflow.com/a/25586869/1143274) na podstawie 'ax.plot_trisurf (..)'. –

10

Po prostu natknąłem się na ten sam problem. Mam równomiernie rozmieszczone dane, które są w 3 macierzach 1-D, zamiast tablic 2D, które chce mieć matplotlib. Moje dane znajdowały się w pandas.DataFrame, więc tutaj jest matplotlib.plot_surface example z modyfikacjami do wykreślania 3 tablic 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') 

To jest oryginalny przykład. Dodanie tego następnego bitu powoduje utworzenie tego samego wykresu z 3 tablic 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() 

Oto uzyskane dane:

enter image description here enter image description here

+0

Zastanawiam się, czy możliwe jest usunięcie linii pojawiających się na powierzchni (obrazek powyżej), mam na myśli, czy można nadać powierzchni lśniący wygląd zamiast łuszczącego wyglądu? Dziękuję Ci. @ stvn66 – diffracteD

+0

@diffracteD, spróbuj użyć mniejszego rozmiaru siatki. Jestem prawie pewien, że to właśnie określa szerokość między konturami. Oceniając na lepszej siatce, należy zasadniczo zmniejszyć "rozmiar piksela" i zwiększyć rozdzielczość, zbliżając się do bardziej płynnego gradientu. –

+0

Czy istnieje sposób na kolorowanie powyższej powierzchni zgodnie z określonymi kategoriami? Na przykład ** Kategoria x, y, z ** jest formatem danych i chciałbym pokolorować powierzchnię przechodzącą przez x, y, z zgodnie z określoną kategorią. –

7

zrobić to z niektórych liniach w python wykorzystujących pandy, fabuła jest piękny!

from mpl_toolkits.mplot3d import Axes3D 
import matplotlib.pyplot as plt 
from matplotlib import cm 
import numpy as np 
import pandas as pd 
from sys import argv 

file = argv[1] 

x,y,z = np.loadtxt(file, unpack=True) 
df = pd.DataFrame({'x': x, 'y': y, 'z': z}) 

fig = plt.figure() 
ax = Axes3D(fig) 
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('teste.pdf') 
plt.show() 

W razie potrzeby można przekazać vmin i vmax w celu zdefiniowania zakresu paska kolorów, np.

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

surface

+0

Ściśle mówiąc, pandy nie są tutaj potrzebne. – downer