แปลงพื้นผิวใน matplotlib


106

ฉันมีรายการของ 3-tuples ที่แสดงถึงชุดของจุดในพื้นที่ 3 มิติ ฉันต้องการพล็อตพื้นผิวที่ครอบคลุมจุดเหล่านี้ทั้งหมด

plot_surfaceฟังก์ชั่นในmplot3dแพคเกจที่ต้องใช้เป็นข้อโต้แย้ง X, Y และ Z จะเป็นอาร์เรย์ 2 มิติ คือplot_surfaceฟังก์ชั่นที่เหมาะสมกับพื้นผิวของพล็อตและฉันจะแปลงข้อมูลของฉันในรูปแบบที่ต้องการหรือไม่

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

8
ต่อไปนี้คือโพสต์ที่เกี่ยวข้อง / คล้ายกัน / ซ้ำกัน: stackoverflow.com/q/3012783/3585557 , stackoverflow.com/q/12423601/3585557 , stackoverflow.com/q/21161884/3585557 , stackoverflow.com/q/26074542/3585557 , stackoverflow.com/q/28389606/3585557 , stackoverflow.com/q/29547687/3585557
Steven C. Howell

โปรดเริ่มแท็กพื้นผิวที่ซ้ำกันทั้งหมดและปิดรายการที่ซ้ำกัน แท็กnumpy , meshสำหรับสิ่งที่เกี่ยวกับการสร้าง meshgrid
smci

คำตอบ:


122

สำหรับพื้นผิวมันแตกต่างจากรายการ 3-tuples เล็กน้อยคุณควรส่งผ่านตารางสำหรับโดเมนในอาร์เรย์ 2d

หากสิ่งที่คุณมีคือรายการของจุด 3 มิติแทนที่จะเป็นฟังก์ชันบางอย่างf(x, y) -> zคุณจะมีปัญหาเพราะมีหลายวิธีในการจัดรูปเมฆจุด 3 มิตินั้นให้เป็นพื้นผิว

นี่คือตัวอย่างพื้นผิวเรียบ:

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
สวัสดีขอบคุณสำหรับสิ่งนี้ คุณช่วยอธิบายให้ละเอียดf(x,y) -> zได้ไหมว่าการมีฟังก์ชั่นทำให้คุณได้รับข้อมูลมากกว่าเพียงแค่ใช้วิธีรายการเหมือนที่ OP มีในตอนแรก
Gregory Kuhn

16
แต่คุณจะทำอย่างไรเมื่อ z เป็นตัวแปรอิสระและไม่ใช่ฟังก์ชันของ x และ y?
Labibah

4
ในกรณีนี้บางทีคุณควรดูplot_trisurfแทน แต่อย่างที่ฉันได้กล่าวไปแล้วมันไม่ใช่เรื่องเล็กน้อยเพราะคุณต้องระบุตำแหน่งพื้นผิวและมีวิธีแก้ปัญหาหลายวิธี ตัวอย่างพื้นฐานให้พิจารณา 4 คะแนนที่กำหนดโดย (0, 0, 0.2), (0, 1, 0), (1, 1, 0.2), (1, 0, 0) ดูจากด้านบนดูเหมือนสี่เหลี่ยมมีรอยพับเล็กน้อย แต่ "พับ" ในแนวทแยงใดเกิดขึ้น? เส้นทแยงมุม "สูง" ที่ 0.2 หรือเส้นทแยงมุม "ต่ำ" เป็น 0 หรือไม่ ทั้งสองเป็นพื้นผิวที่ถูกต้อง! ดังนั้นคุณต้องเลือกอัลกอริทึมการหาสามเหลี่ยมก่อนจึงจะมีโซลูชันที่กำหนดไว้อย่างดี
Wim

ทำไมจาก mpl_toolkits.mplot3d import Axes3D แต่ Axes3D ไม่ได้ใช้ที่ใดก็ได้ในโค้ดด้านบน
絢瀬絵里

5
การนำเข้านี้มีผลข้างเคียง การใช้ kwarg projection='3d'ในการโทรfig.add_subplotจะไม่สามารถใช้งานได้หากไม่มีการนำเข้านี้
Wim

35

คุณสามารถอ่านข้อมูลโดยตรงจากไฟล์และพล็อตบางไฟล์

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

หากจำเป็นคุณสามารถส่ง vmin และ vmax เพื่อกำหนดช่วงแถบสีได้เช่น

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

พื้นผิว

ส่วนโบนัส

ฉันสงสัยว่าจะทำแผนโต้ตอบบางอย่างได้อย่างไรในกรณีนี้ด้วยข้อมูลเทียม

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
พูดอย่างเคร่งครัดแพนด้าไม่จำเป็นที่นี่
downer

ฉันมีช่วงเวลาที่ยากลำบากในการทำซ้ำพล็อตนี้ ค่าตัวอย่าง (น้อยกว่า) จะเป็นอย่างไรเพื่อให้บรรลุเป้าหมายนี้
JRsz

21

ฉันเพิ่งเจอปัญหาเดียวกันนี้ ฉันได้เว้นระยะข้อมูลที่อยู่ใน 3 1-D อาร์เรย์แทนที่จะเป็น 2 มิติอาร์เรย์เท่า ๆ กันว่าmatplotlib's plot_surfaceความต้องการ ข้อมูลของฉันเกิดขึ้นในpandas.DataFrameที่นี้นี่คือmatplotlib.plot_surfaceตัวอย่างที่มีการแก้ไขพล็อตอาร์เรย์ 3 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')

นั่นคือตัวอย่างเดิม การเพิ่มบิตถัดไปนี้จะสร้างพล็อตเดียวกันจากอาร์เรย์ 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()

นี่คือตัวเลขผลลัพธ์:

ป้อนคำอธิบายภาพที่นี่ ป้อนคำอธิบายภาพที่นี่


ฉันสงสัยว่ามันเป็นไปได้ไหมที่จะลบเส้นที่มาบนพื้นผิว (ภาพด้านบน) ฉันหมายความว่ามันเป็นไปได้ไหมที่จะให้พื้นผิวมันวาวแทนที่จะเป็นเกล็ด? ขอบคุณ. @ stvn66
diffracteD

@diffracteD ลองใช้ขนาดตารางที่เล็กลง ฉันเกือบจะแน่ใจแล้วว่านั่นคือสิ่งที่กำหนดความกว้างระหว่างรูปทรง โดยการประเมินบนเส้นตารางที่ละเอียดขึ้นคุณควรจะลด "ขนาดพิกเซล" เป็นหลักและเพิ่มความละเอียดโดยเข้าใกล้การไล่ระดับสีที่นุ่มนวลขึ้น
Steven C. Howell

มีวิธีลงสีพื้นผิวด้านบนตามหมวดหมู่เฉพาะหรือไม่? สำหรับอดีต หมวดหมู่ x, y, zคือรูปแบบข้อมูลและฉันต้องการให้สีพื้นผิวผ่าน x, y, z ตามหมวดหมู่เฉพาะ
Rudresh Ajgaonkar

@RudreshAjgaonkar คุณควรใช้คำสั่งพล็อตแยกกันสามคำสั่งสำหรับแต่ละหมวดหมู่โดยใช้สีอะไรก็ได้ที่คุณต้องการสำหรับแต่ละสามคำสั่ง
Steven C. Howell

ขอรหัสตัวอย่างได้ไหม ฉันค่อนข้างใหม่สำหรับ matplotlib และ python
Rudresh Ajgaonkar

4

Emanuel มีคำตอบที่ฉัน (และอาจจะอีกหลายคน) กำลังมองหา หากคุณมีข้อมูล 3 มิติที่กระจัดกระจายใน 3 อาร์เรย์แยกกันแพนด้าเป็นตัวช่วยที่เหลือเชื่อและทำงานได้ดีกว่าตัวเลือกอื่น ๆ หากต้องการอธิบายอย่างละเอียดสมมติว่า x, y, z ของคุณเป็นตัวแปรตามอำเภอใจ ในกรณีของฉันสิ่งเหล่านี้คือ c, แกมมาและข้อผิดพลาดเนื่องจากฉันกำลังทดสอบเครื่องเวกเตอร์สนับสนุน มีทางเลือกมากมายในการลงจุดข้อมูล:

  • scatter3D (cParams, gammas, avg_errors_array) - ใช้งานได้ แต่ง่ายเกินไป
  • plot_wireframe (cParams, gammas, avg_errors_array) - ใช้งานได้ แต่จะดูน่าเกลียดหากข้อมูลของคุณไม่ได้รับการจัดเรียงอย่างสวยงามเช่นเดียวกับในกรณีที่มีข้อมูลทางวิทยาศาสตร์จริงจำนวนมาก
  • ax.plot3D (cParams, gammas, avg_errors_array) - คล้ายกับ wireframe

โครงร่างโครงร่างของข้อมูล

โครงร่างโครงร่างของข้อมูล

การกระจายข้อมูล 3 มิติ

การกระจายข้อมูล 3 มิติ

รหัสมีลักษณะดังนี้:

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

นี่คือผลลัพธ์สุดท้าย:

plot_trisurf ของข้อมูล xyz


3

ตรวจสอบตัวอย่างอย่างเป็นทางการ X, Y และ Z เป็นอาร์เรย์ 2d แน่นอน numpy.meshgrid () เป็นวิธีง่ายๆในการรับ 2d x, y mesh จากค่า 1d x และ y

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

นี่คือวิธี pythonic ในการแปลง 3-tuples ของคุณเป็น 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)

นี่คือ mtaplotlib delaunay triangulation (interpolation) ซึ่งจะแปลง 1d x, y, z เป็นสิ่งที่สอดคล้อง (?):

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


ไม่ ... XYZ เป็น 2 มิติในตัวอย่างนั้น
Wim

ฉันยืนแก้ไข ใช้ meshgrid () หากข้อมูลของคุณมีระยะห่างเท่ากันเช่นในตัวอย่างที่เชื่อมโยง สอดแทรกเช่นด้วย griddata () หากข้อมูลของคุณไม่เว้นระยะห่างเท่ากัน
Dima Tisnek

2

เพียงเพื่อเพิ่มความคิดเพิ่มเติมซึ่งอาจช่วยผู้อื่นที่มีปัญหาประเภทโดเมนผิดปกติ สำหรับสถานการณ์ที่ผู้ใช้มีเวกเตอร์ / รายการสามรายการ x, y, z แสดงโซลูชัน 2 มิติโดยที่ z จะถูกพล็อตบนตารางสี่เหลี่ยมเป็นพื้นผิวความคิดเห็น 'plot_trisurf ()' โดย ArtifixR จะใช้ได้ ตัวอย่างที่คล้ายกัน แต่ไม่มีโดเมนสี่เหลี่ยมคือ:

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

รหัสข้างต้นก่อให้เกิด:

พล็อตพื้นผิวสำหรับปัญหากริดที่ไม่ใช่สี่เหลี่ยม

อย่างไรก็ตามนี่อาจไม่สามารถแก้ปัญหาทั้งหมดได้โดยเฉพาะอย่างยิ่งเมื่อปัญหาถูกกำหนดบนโดเมนที่ผิดปกติ นอกจากนี้ในกรณีที่โดเมนมีพื้นที่เว้าตั้งแต่หนึ่งส่วนขึ้นไปการกำหนดรูปสามเหลี่ยมที่ล่าช้าอาจส่งผลให้เกิดรูปสามเหลี่ยมปลอมที่อยู่ภายนอกโดเมน ในกรณีเช่นนี้ต้องนำสามเหลี่ยมที่หลอกลวงเหล่านี้ออกจากรูปสามเหลี่ยมเพื่อให้ได้การแสดงพื้นผิวที่ถูกต้อง สำหรับสถานการณ์เหล่านี้ผู้ใช้อาจต้องรวมการคำนวณการกำหนดรูปสามเหลี่ยมเดลาวันอย่างชัดเจนเพื่อให้สามารถลบรูปสามเหลี่ยมเหล่านี้ได้โดยทางโปรแกรม ภายใต้สถานการณ์เหล่านี้รหัสต่อไปนี้สามารถแทนที่รหัสแปลงก่อนหน้า:


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

ตัวอย่างแผนภาพแสดงไว้ด้านล่างวิธีการแก้ปัญหา 1) ที่มีสามเหลี่ยมปลอมและ 2) ที่ถูกลบออก:

ป้อนคำอธิบายภาพที่นี่

นำสามเหลี่ยมออก

ฉันหวังว่าข้อมูลข้างต้นอาจช่วยผู้ที่มีสถานการณ์ความเว้าในข้อมูลโซลูชันได้


1

ใน Matlab ผมทำอะไรที่คล้ายกันโดยใช้delaunayฟังก์ชั่นบนx, ycoords เท่านั้น (ไม่ได้z) แล้วพล็อตที่มีtrimeshหรือtrisurfใช้zเป็นความสูง

SciPy มีคลาสDelaunayซึ่งขึ้นอยู่กับไลบรารี QHull พื้นฐานเดียวกันกับdelaunayฟังก์ชันของ Matlab ดังนั้นคุณควรได้ผลลัพธ์ที่เหมือนกัน

จากนั้นควรมีโค้ดสองสามบรรทัดในการแปลงPlotting 3D Polygons นี้ในตัวอย่างpython-matplotlibให้เป็นสิ่งที่คุณต้องการบรรลุตามDelaunayที่ระบุข้อมูลจำเพาะของรูปหลายเหลี่ยมสามเหลี่ยมแต่ละรูปแบบ


ดูคำตอบนี้ax.plot_trisurf(..)อยู่บนพื้นฐานของ
Evgeni Sergeev

0

ไม่สามารถสร้างพื้นผิว 3 มิติโดยตรงโดยใช้ข้อมูลของคุณ ฉันจะแนะนำให้คุณสามารถสร้างรูปแบบการแก้ไขโดยใช้เครื่องมือบางอย่างเช่นpykridge กระบวนการนี้ประกอบด้วยสามขั้นตอน:

  1. ฝึกโมเดลการแก้ไขโดยใช้ pykridge
  2. สร้างตารางจากXและYใช้meshgrid
  3. แก้ไขค่าสำหรับ Z

เมื่อสร้างกริดและZค่าที่เกี่ยวข้องแล้วตอนนี้คุณก็พร้อมที่จะใช้งานplot_surfaceแล้ว โปรดทราบว่าขึ้นอยู่กับขนาดของข้อมูลของคุณmeshgridฟังก์ชันสามารถทำงานได้ชั่วขณะหนึ่ง วิธีแก้ปัญหาคือการสร้างตัวอย่างที่เว้นระยะเท่า ๆ กันโดยใช้np.linspaceสำหรับXและYแกนจากนั้นใช้การแก้ไขเพื่ออนุมานZค่าที่จำเป็น หากเป็นเช่นนั้นค่าที่ถูกแก้ไขอาจแตกต่างไปจากเดิมZเนื่องจากXและYมีการเปลี่ยนแปลง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.