đồ thị bề mặt trong matplotlib


104

Tôi có một danh sách gồm 3 bộ giá trị đại diện cho một tập hợp các điểm trong không gian 3D. Tôi muốn vẽ một bề mặt bao gồm tất cả những điểm này.

Các plot_surfacechức năng trong mplot3dgói đòi hỏi như các đối số X, Y và Z là mảng 2ngày. Có plot_surfacephải chức năng phù hợp để 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 bắt buộc?

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


Vui lòng bắt đầu gắn thẻ tất cả các bề mặt trùng lặp đó và đóng các bản sao vào nhau. Cũng gắn thẻ numpy , lưới cho những cái sắp tạo lưới.
smci

Câu trả lời:


120

Đối với các bề mặt, nó hơi khác một chút so với danh sách 3 bộ dữ liệu, bạn nên chuyển vào lưới cho miền trong các mảng 2d.

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 vấn đề vì có nhiều cách để tam giác đám mây điểm 3d đó thành một bề mặt.

Đây là một ví dụ về bề mặt nhẵn:

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

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

4
Trong trường hợp này, có lẽ bạn nên xem xét plot_trisurfthay thế. Nhưng như tôi đã đề cập, nó không phải là nhỏ vì bạn cần phải phân chia bề mặt và có nhiều giải pháp. Ví dụ cơ bản, chỉ xem xét 4 điểm 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 nếp gấp nhẹ ở trong. Nhưng "nếp gấp" xảy ra dọc theo đường chéo nào? Đó là đường chéo "cao" tại 0,2 hay đường chéo "thấp" tại 0? 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 tam giác trước khi bạn có một giải pháp được xác định rõ ràng.
wim

Tại sao từ mpl_toolkits.mplot3d nhập Axes3D, nhưng Axes3D không được sử dụng ở bất kỳ đâu trong đoạn mã trên?
絢 瀬 絵 里

5
Nhập khẩu này có tác dụng phụ. Việc sử dụng kwarg projection='3d'trong cuộc gọi fig.add_subplotsẽ không khả dụng nếu không có lần nhập này.
wim

33

Bạn có thể đọc dữ liệu trực tiếp từ một số tệp và âm mưu

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

Nếu cần, bạn có thể chuyển vmin và vmax để xác định phạm vi thanh màu, ví dụ:

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

bề mặt

Phần thưởng

Tôi đã tự hỏi làm thế nào để thực hiện một số âm mưu tương tác, trong trường hợp này là với dữ liệu nhân tạo

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
nói đúng ra, gấu trúc là không cần thiết ở đây.
downer

Tôi gặp khó khăn khi tái tạo lại cốt truyện này. Một số giá trị mẫu (nhỏ hơn) sẽ là gì để đạt được điều này?
JRsz

21

Tôi vừa gặp phải vấn đề tương tự này. Tôi đã đều nhau dữ liệu đó là trong 3 mảng 1-D thay vì các mảng 2-D mà matplotlib's plot_surfacemong muốn. Dữ liệu của tôi tình cờ nằm ​​trong một pandas.DataFramevì vậy, đây là matplotlib.plot_surfaceví dụ với các sửa đổi cho biểu đồ 3 mảng 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')

Đó là ví dụ ban đầu. Thêm bit tiếp theo này vào sẽ tạo ra cùng một âm mưu 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à các số liệu kết quả:

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây


Tôi đã tự hỏi liệu có thể loại bỏ các đường nổi trên bề mặt (hình trên), ý tôi là có thể làm cho bề mặt bóng bẩy thay vì có vảy không? cảm ơn bạn. @ stvn66
diffracteD

@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 rằng đó là những gì thiết lập chiều rộng giữa các đường viền. Bằng cách đánh giá trên một lưới mịn hơn, về cơ bản bạn phải giảm "kích thước pixel" và tăng độ phân giải, tiến tới một gradient mượt mà hơn.
Steven C. Howell

Có cách nào để tạo màu cho bề mặt trên theo từng loại cụ thể không? Đối với người yêu cũ. 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 loại cụ thể.
Rudresh Ajgaonkar

@RudreshAjgaonkar, bạn sẽ có thể sử dụng ba lệnh cốt truyện riêng biệt, một lệnh cho mỗi danh mục của bạn, sử dụng bất kỳ màu nào bạn muốn cho mỗi trong ba lệnh.
Steven C. Howell,

bạn có thể cung cấp một mã mẫu xin vui lòng? tôi khá mới với matplotlib và python.
Rudresh Ajgaonkar

4

Chỉ để kêu gọi, Emanuel đã có câu trả lời mà tôi (và có lẽ nhiều người khác) đang tìm kiếm. Nếu bạn có dữ liệu phân tán 3d trong 3 mảng riêng biệt, gấu trúc là một trợ giúp đáng kinh ngạc và hoạt động tốt hơn nhiều so với các tùy chọn khác. Để giải thích rõ hơn, giả sử x, y, z của bạn là một số biến tùy ý. Trong trường hợp của tôi, đây là c, gamma và lỗi vì tôi đang thử nghiệm một máy vector hỗ trợ. Có nhiều lựa chọn tiềm năng để vẽ dữ liệu:

  • scatter3D (cParams, gammas, avg_errors_array) - điều này hoạt động nhưng quá đơn giản
  • plot_wireframe (cParams, gammas, avg_errors_array) - điều này hoạt động, nhưng sẽ trông xấu xí nếu dữ liệu của bạn không được sắp xếp đẹp mắt, như có thể xảy ra với khối lượng lớn dữ liệu khoa học thực
  • ax.plot3D (cParams, gammas, avg_errors_array) - tương tự như wireframe

Biểu đồ khung dây của dữ liệu

Biểu đồ khung dây của dữ liệu

Phân tán dữ liệu 3d

Phân tán dữ liệu 3d

Mã trông như thế này:

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

Đây là kết quả cuối cùng:

plot_trisurf của dữ liệu xyz


3

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 để lấy lưới 2d x, y từ các giá trị 1d x và y.

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

đây là cách pythonic để chuyển đổi 3 bộ giá trị của bạn thành 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)

Đây là triangulation mtaplotlib delaunay (nội suy), nó chuyển đổi 1d x, y, z thành một cái gì đó tuân thủ (?):

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


Không ... XYZ là 2 chiều trong ví dụ đó.
wim

Tôi đứng sửa lại. Sử dụng meshgrid () nếu dữ liệu của bạn 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 cách đều nhau.
Dima Tisnek

1

Trong Matlab, tôi đã làm điều gì đó tương tự bằng cách sử dụng delaunayhàm trên x, ychỉ coords (không phải z), sau đó vẽ biểu đồ với trimeshhoặc trisurf, sử dụng zlàm chiều cao.

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

Từ đó, sẽ có một vài dòng mã để chuyển đổi Đa giác 3D Vẽ đồ thị này trong ví dụ python-matplotlib thành những gì bạn muốn đạt được, vì Delaunaycung cấp cho bạn đặc điểm kỹ thuật của từng đa giác tam giác.


Xem câu trả lời này dựa trên ax.plot_trisurf(..).
Evgeni Sergeev

1

Chỉ để thêm một số suy nghĩ có thể giúp những người khác có vấn đề về loại miền không đều. Đối với trường hợp người dùng có ba vectơ / danh sách, x, y, z đại diện cho giải pháp 2D trong đó z sẽ được vẽ trên một lưới hình chữ nhật làm bề mặt, các nhận xét 'plot_trisurf ()' của ArtifixR có thể áp dụng. Một ví dụ tương tự nhưng với miền không phải là hình chữ nhật là:

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

Đoạn mã trên tạo ra:

Biểu đồ bề mặt cho vấn đề lưới không phải hình chữ nhật

Tuy nhiên, điều này có thể không giải quyết được tất cả các vấn đề, đặc biệt khi vấn đề được xác định trên một miền bất thường. Ngoài ra, trong trường hợp miền có một hoặc nhiều vùng lõm, tam giác sai lệch có thể tạo ra các tam giác giả bên ngoài miền. Trong những trường hợp như vậy, các hình tam giác giả này phải được loại bỏ khỏi hình tam giác để đạt được biểu diễn bề mặt chính xác. Đối với những tình huống này, người dùng có thể phải đưa vào tính toán tam giác sai lệch một cách rõ ràng để có thể loại bỏ các tam giác này theo chương trình. Trong những trường hợp này, mã sau có thể thay thế mã cốt truyện trước đó:


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

Các biểu đồ ví dụ được đưa ra dưới đây minh họa giải pháp 1) với tam giác giả và 2) nơi chúng đã bị loại bỏ:

nhập mô tả hình ảnh ở đây

hình tam giác bị loại bỏ

Tôi hy vọng những điều trên có thể giúp ích cho những người có tình huống khó hiểu trong dữ liệu giải pháp.


0

Không thể trực tiếp tạo bề mặt 3d bằng dữ liệu của bạn. Tôi khuyên bạn nên xây dựng mô hình nội suy bằng một số công cụ như pykridge . Quá trình này sẽ bao gồm ba bước:

  1. Đào tạo mô hình nội suy bằng cách sử dụng pykridge
  2. Xây dựng lưới từ XYsử dụngmeshgrid
  3. Nội suy các giá trị cho Z

Sau khi tạo lưới của bạn và các Zgiá trị tương ứng , bây giờ bạn đã sẵn sàng để sử dụng plot_surface. Lưu ý rằng tùy thuộc vào kích thước dữ liệu của bạn, meshgridhàm có thể chạy trong một thời gian. Cách giải quyết là tạo các mẫu cách đều nhau bằng np.linspacefor XYaxis, sau đó áp dụng phép nội suy để suy ra các Zgiá trị cần thiết . Nếu vậy, các giá trị được nội suy có thể khác với giá trị ban đầu ZXYđã thay đổi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.