plot permukaan di matplotlib


104

Saya memiliki daftar 3-tupel yang mewakili satu set titik dalam ruang 3D. Saya ingin menggambar permukaan yang mencakup semua titik ini.

The plot_surfacefungsi dalam mplot3dpaket memerlukan sebagai argumen X, Y dan Z menjadi array 2d. Apakah plot_surfacefungsi yang tepat untuk memplot permukaan dan bagaimana cara mengubah data saya ke dalam format yang diperlukan?

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


Mulailah memberi tag pada semua permukaan duplikat tersebut , dan tutup duplikat tersebut satu sama lain. Juga beri tag numpy , mesh untuk yang berhubungan dengan pembuatan meshgrid.
smci

Jawaban:


120

Untuk permukaan ini sedikit berbeda dari daftar 3-tupel, Anda harus meneruskan grid untuk domain dalam array 2d.

Jika yang Anda miliki hanyalah daftar titik 3d, bukan beberapa fungsi f(x, y) -> z, Anda akan mendapat masalah karena ada beberapa cara untuk melakukan triangulasi titik awan 3d tersebut ke permukaan.

Berikut contoh permukaan yang halus:

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
Hai, terima kasih untuk ini. Bisakah Anda menguraikan tentang bagaimana memiliki suatu fungsi f(x,y) -> zmemberi Anda lebih banyak informasi daripada hanya menggunakan pendekatan daftar seperti yang awalnya dimiliki OP.
Gregory Kuhn

16
Tapi apa yang Anda lakukan jika z adalah variabel independen dan bukan fungsi dari x dan y?
Labibah

4
Dalam hal ini, mungkin Anda harus melihatnya plot_trisurf. Tapi seperti yang saya sebutkan, ini tidak sepele karena Anda perlu melakukan triangulasi permukaan dan ada banyak solusi. Sebagai contoh dasar, pertimbangkan hanya 4 poin yang diberikan oleh (0, 0, 0.2), (0, 1, 0), (1, 1, 0.2), (1, 0, 0). Dilihat dari atas, terlihat seperti persegi dengan sedikit lipatan di dalamnya. Tapi sepanjang diagonal manakah "lipatan" itu terjadi? Apakah diagonal "tinggi" pada 0,2 atau diagonal "rendah" pada 0? Keduanya adalah permukaan yang valid! Jadi, Anda perlu memilih algoritme triangulasi sebelum Anda memiliki solusi yang terdefinisi dengan baik.
wim

Mengapa dari mpl_toolkits.mplot3d mengimpor Axes3D, namun Axes3D tidak digunakan di mana pun dalam kode di atas?
絢 瀬 絵 里

5
Impor ini memiliki efek samping. Menggunakan kwarg projection='3d'dalam panggilan fig.add_subplottidak akan tersedia tanpa impor ini.
wim

34

Anda dapat membaca data langsung dari beberapa file dan plot

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

Jika perlu, Anda dapat meneruskan vmin dan vmax untuk menentukan rentang colorbar, mis

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

permukaan

Bagian Bonus

Saya bertanya-tanya bagaimana melakukan beberapa plot interaktif, dalam hal ini dengan data buatan

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
tegasnya, panda tidak diperlukan di sini.
downer

Saya kesulitan mereproduksi plot ini. Berapa nilai sampel (lebih kecil) untuk mencapai ini?
JRsz

21

Saya baru saja menemukan masalah yang sama ini. Saya telah merata spasi data yang ada di 3 array 1-D bukan array 2-D yang matplotlib's plot_surfacekeinginan. Data saya kebetulan ada di pandas.DataFramesini, jadi inilah matplotlib.plot_surfacecontoh dengan modifikasi untuk memplot 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')

Itu contoh aslinya. Menambahkan bit berikutnya ini akan membuat plot yang sama dari array 3 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()

Berikut angka-angka yang dihasilkan:

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini


Saya bertanya-tanya apakah mungkin untuk menghilangkan garis-garis yang muncul di permukaan (gambar di atas), maksud saya apakah mungkin untuk memberikan permukaan tampilan yang glossy dan bukan tampilan bersisik? Terima kasih. @ stvn66
difraksi

@diffracteD, coba gunakan ukuran petak yang lebih kecil. Saya hampir yakin itulah yang mengatur lebar antara kontur. Dengan mengevaluasi pada petak yang lebih halus, pada dasarnya Anda harus mengurangi "ukuran piksel" dan meningkatkan resolusi, mendekati gradien yang lebih halus.
Steven C. Howell

Adakah cara untuk mewarnai permukaan di atas menurut kategori tertentu? Misalnya. Kategori x, y, z adalah format datanya dan saya ingin mewarnai permukaan yang melewati x, y, z menurut kategori tertentu.
Rudresh Ajgaonkar

@RudreshAjgaonkar, Anda harus dapat menggunakan tiga perintah plot terpisah, satu untuk setiap kategori Anda, menggunakan pewarnaan apa pun yang Anda inginkan untuk masing-masing dari ketiganya.
Steven C. Howell

bisakah Anda memberikan kode contoh? saya cukup baru mengenal matplotlib dan python.
Rudresh Ajgaonkar

4

Sekadar berpadu, Emanuel memiliki jawaban yang saya (dan mungkin banyak lainnya) cari. Jika Anda memiliki data 3d yang tersebar dalam 3 larik terpisah, panda adalah bantuan luar biasa dan bekerja jauh lebih baik daripada opsi lainnya. Untuk menguraikan, anggaplah x, y, z Anda adalah beberapa variabel sembarang. Dalam kasus saya ini adalah c, gamma, dan kesalahan karena saya sedang menguji mesin vektor dukungan. Ada banyak pilihan potensial untuk memplot data:

  • scatter3D (cParams, gammas, avg_errors_array) - ini berfungsi tetapi terlalu sederhana
  • plot_wireframe (cParams, gammas, avg_errors_array) - ini berfungsi, tetapi akan terlihat jelek jika data Anda tidak diurutkan dengan baik, seperti halnya kasus dengan potongan besar data ilmiah nyata
  • ax.plot3D (cParams, gammas, avg_errors_array) - mirip dengan wireframe

Plot data wireframe

Plot data wireframe

3d penyebaran data

3d penyebaran data

Kodenya terlihat seperti ini:

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

Inilah hasil akhirnya:

plot_trisurf data xyz


3

periksa contoh resminya. X, Y dan Z memang array 2d, numpy.meshgrid () adalah cara sederhana untuk mendapatkan 2d x, y mesh dari nilai 1d x dan y.

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

inilah cara pythonic untuk mengubah 3-tuple Anda menjadi 3 1d array.

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)

Berikut triangulasi penundaan mtaplotlib (interpolasi), ini mengubah 1d x, y, z menjadi sesuatu yang sesuai (?):

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


Tidak ... XYZ adalah 2 dimensi dalam contoh itu.
wim

Saya berdiri dikoreksi. Gunakan meshgrid () jika data Anda memiliki jarak yang seragam, seperti dalam contoh yang ditautkan. Interpolasi misalnya dengan griddata () jika data Anda jika tidak spasi seragam.
Dima Tisnek

1

Di Matlab saya melakukan hal serupa dengan menggunakan delaunayfungsi pada x, yhanya coord (bukan z), lalu memplot dengan trimeshatau trisurf, menggunakan zsebagai ketinggian.

SciPy memiliki kelas Delaunay , yang didasarkan pada pustaka QHull dasar yang sama dengan delaunayfungsi Matlab , jadi Anda harus mendapatkan hasil yang identik.

Dari sana, harus ada beberapa baris kode untuk mengubah Plotting 3D Polygons ini dalam contoh python-matplotlib menjadi apa yang ingin Anda capai, seperti yang Delaunaymemberi Anda spesifikasi dari setiap poligon segitiga.


Lihat jawaban ini berdasarkan ax.plot_trisurf(..).
Evgeni Sergeev

1

Hanya untuk menambahkan beberapa pemikiran lebih lanjut yang dapat membantu orang lain dengan masalah jenis domain tidak teratur. Untuk situasi di mana pengguna memiliki tiga vektor / daftar, x, y, z mewakili solusi 2D di mana z akan diplot pada kisi persegi panjang sebagai permukaan, komentar 'plot_trisurf ()' oleh ArtifixR dapat diterapkan. Contoh serupa tetapi dengan domain non persegi panjang adalah:

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

Kode di atas menghasilkan:

Plot permukaan untuk masalah grid non-persegi panjang

Namun, ini mungkin tidak menyelesaikan semua masalah, khususnya di mana masalah didefinisikan pada domain tidak beraturan. Juga, dalam kasus di mana domain memiliki satu atau lebih area cekung, triangulasi penundaan dapat mengakibatkan munculnya segitiga palsu di luar domain. Dalam kasus seperti itu, segitiga nakal ini harus dihilangkan dari triangulasi untuk mencapai representasi permukaan yang benar. Untuk situasi ini, pengguna mungkin harus secara eksplisit menyertakan penghitungan triangulasi penundaan sehingga segitiga ini dapat dihapus secara terprogram. Dalam keadaan ini, kode berikut dapat menggantikan kode plot sebelumnya:


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

Contoh plot diberikan di bawah ini menggambarkan solusi 1) dengan segitiga palsu, dan 2) di mana mereka telah dihilangkan:

masukkan deskripsi gambar di sini

segitiga dihapus

Saya berharap hal di atas dapat membantu orang-orang dengan situasi cekung dalam data solusi.


0

Tidak mungkin membuat permukaan 3d secara langsung menggunakan data Anda. Saya akan merekomendasikan Anda untuk membangun model interpolasi menggunakan beberapa alat seperti pykridge . Prosesnya akan mencakup tiga langkah:

  1. Latih model interpolasi menggunakan pykridge
  2. Bangun kisi dari Xdan Ygunakanmeshgrid
  3. Interpolasi nilai untuk Z

Setelah membuat kisi Anda dan nilai yang sesuai Z, sekarang Anda siap untuk menggunakannya plot_surface. Perhatikan bahwa tergantung pada ukuran data Anda, meshgridfungsi tersebut dapat berjalan untuk sementara waktu. Solusi adalah untuk membuat sampel merata spasi menggunakan np.linspaceuntuk Xdan Ykapak, kemudian menerapkan interpolasi untuk menyimpulkan diperlukan Znilai-nilai. Jika demikian, nilai interpolasi mungkin berbeda dari aslinya Zkarena Xdan Ytelah berubah.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.