2012-02-07 51 views
63

Tôi có một danh sách 3 bộ dữ liệu đại diện cho một tập các điểm trong không gian 3D. Tôi muốn vẽ một bề mặt bao phủ tất cả các điểm này. Hàm plot_surface trong gói mplot3d yêu cầu các đối số X, Y và Z là các mảng 2d. Có phải plot_surface là hàm đúng để vẽ bề mặt và làm cách nào để chuyển đổi dữ liệu của tôi sang định dạng được yêu cầu?các ô bề mặt trong matplotlib

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

+2

Dưới đây là một số liên quan/tương tự/bài trùng lặp: 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. –

Trả lời

70

Đối với bề mặt đó là một chút khác biệt so với một danh sách các 3-tuples, bạn nên vượt qua trong một mạng lưới cho các tên miền trong mảng 2ngày.

Nếu tất cả những gì bạn có là danh sách các điểm 3d, thay vì một số chức năng f(x, y) -> z, thì bạn sẽ gặp sự cố vì có nhiều cách để tam giác đám mây điểm 3d đó thành một bề mặt.

Dưới đây là một bề mặt nhẵn ví dụ:

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

Xin chào, cảm ơn vì điều này. Bạn có thể vui lòng giải thích về cách có một hàm 'f (x, y) -> z' giúp bạn có thêm thông tin hơn là chỉ sử dụng cách tiếp cận danh sách giống như OP ban đầu. –

+4

Nhưng bạn sẽ làm gì khi z là một biến độc lập và không phải là hàm của x và y? – Labibah

+2

Trong trường hợp này, có lẽ bạn nên nhìn vào 'plot_trisurf' thay thế. Nhưng như tôi đã đề cập, nó không tầm thường bởi vì bạn cần phải triangulate bề mặt và có nhiều giải pháp. Như một ví dụ cơ bản, chỉ xem xét 4 điểm được cho bởi (0, 0, 0.2), (0, 1, 0), (1, 1, 0.2), (1, 0, 0). Nhìn từ trên cao, nó trông giống như một hình vuông với một chút nếp gấp trong đó. * Nhưng dọc theo đường chéo nào thì "nếp gấp" xuất hiện? * Có phải đường chéo "cao" ở mức 0,2 hay đường chéo "thấp" ở 0 không? Cả hai đều là bề mặt hợp lệ! Vì vậy, bạn cần phải chọn một thuật toán triangulation trước khi bạn có một giải pháp được xác định rõ. – wim

4

kiểm tra ví dụ chính thức. X, Y và Z thực sự là mảng 2d, numpy.meshgrid() là một cách đơn giản để có được 2d x, y lưới trong số 1d x và y giá trị.

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

đây là cách pythonic để chuyển đổi 3-tuples của bạn tới 3 mảng 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) 

Dưới đây là mtaplotlib Delaunay triangulation (nội suy), nó chuyển đổi x 1d, y, z vào một cái gì đó phù hợp (?):

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

+0

Không ... X Y Z là 2 chiều trong ví dụ đó. – wim

+0

Tôi đã sửa. Sử dụng meshgrid() nếu dữ liệu của bạn có khoảng cách đều nhau, như trong ví dụ được liên kết. Nội suy, ví dụ: với griddata() nếu dữ liệu của bạn nếu không có khoảng cách thống nhất. –

1

Trong Matlab tôi đã làm một cái gì đó tương tự bằng cách sử dụng delaunay chức năng trên chỉ x, y chỉ coords (không phải là z), sau đó vẽ với trimesh hoặc trisurf, sử dụng độ cao z.

SciPy có lớp Delaunay, dựa trên cùng thư viện QHull cơ bản mà chức năng delaunay của Matlab là, vì vậy bạn sẽ nhận được kết quả giống hệt nhau.

Từ đó, cần có một vài dòng mã để chuyển đổi ví dụ Plotting 3D Polygons in python-matplotlib này thành những gì bạn muốn đạt được, như Delaunay cung cấp cho bạn đặc điểm kỹ thuật của mỗi đa giác hình tam giác.

+0

Xem [câu trả lời này] (http://stackoverflow.com/a/25586869/1143274) dựa trên 'ax.plot_trisurf (..)'. –

10

Tôi vừa gặp phải vấn đề tương tự này. Tôi có dữ liệu khoảng cách đều nhau trong các mảng 3 1-D thay vì các mảng 2-D mà plot_surface muốn matplotlib muốn. Dữ liệu của tôi đã xảy ra ở một số pandas.DataFrame vì vậy, đây là số matplotlib.plot_surface example với các sửa đổi đối với mảng 3 1-D lô.

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

Đó là ví dụ ban đầu. Thêm bit tiếp theo này trên tạo ra cùng một lô từ 3 mảng 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() 

Dưới đây là những con số kết quả:

enter image description here enter image description here

+0

Tôi đã tự hỏi nếu có thể loại bỏ các dòng trên bề mặt (hình ảnh trên), tôi có nghĩa là nó có thể cung cấp cho bề mặt một cái nhìn bóng thay vì xuất hiện vảy? cảm ơn bạn. @ stvn66 – diffracteD

+0

@diffracteD, hãy thử sử dụng kích thước lưới nhỏ hơn. Tôi gần như chắc chắn đó là những gì thiết lập chiều rộng giữa các đường nét. Bằng cách đánh giá trên một mạng lưới mịn hơn, về cơ bản bạn nên giảm "kích thước pixel" và tăng độ phân giải, tiếp cận gradient mượt mà hơn. –

+0

Có cách nào tô màu bề mặt trên theo các danh mục cụ thể không? Ví dụ: ** Loại x, y, z ** là định dạng dữ liệu và tôi muốn tô màu bề mặt đi qua x, y, z theo một danh mục cụ thể. –

7

Tôi làm điều này với một số dòng trong python sử dụng gấu trúc, cốt truyện là xinh đẹp!

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

Nếu cần, bạn có thể vượt qua vmin và vmax để xác định dải màu, ví dụ:

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

surface

+0

nói đúng, gấu trúc là không cần thiết ở đây. – downer

Các vấn đề liên quan