поверхневі ділянки в матплотлібі


104

У мене є список 3-х кортежів, що представляють набір точок у 3D-просторі. Я хочу побудувати поверхню, яка охоплює всі ці точки.

plot_surfaceФункція в mplot3dпакеті вимагає в якості аргументів X, Y і Z , щоб бути 2d масивів. Чи plot_surfaceє правильною функцією для накреслення поверхні та як я можу перетворити свої дані у потрібний формат?

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


Почніть позначати всі ці дублікати поверхнею та закривати дублікати один до одного. Також тег numpy , mesh для тих, що стосуються генерації meshgrid.
smci

Відповіді:


120

Для поверхонь це дещо інше, ніж перелік 3-кортежів, вам слід пройти в сітці для домену в 2d-масивах.

Якщо все, що ви маєте, - це перелік 3d-точок, а не якась функція f(x, y) -> z, тоді у вас виникне проблема, оскільки існує три способи триангуляції цієї хмари 3d-точки в поверхню.

Ось приклад гладкої поверхні:

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отримує більше інформації, ніж просто використання спискового підходу, як це було в ОР.
Григорій Кун

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? Обидва - дійсні поверхні! Тому вам потрібно вибрати алгоритм тріангуляції, перш ніж мати чітко визначене рішення.
Вім

Чому з mpl_toolkits.mplot3d імпортують Axes3D, але Axes3D не використовується ніде в коді вище?
絢 瀬 絵 里

5
Цей імпорт має побічні ефекти. Використання kwarg projection='3d'у виклику fig.add_subplotбуде недоступним без цього імпорту.
вім

33

Ви можете читати дані безпосередньо з якогось файлу та сюжету

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
строго кажучи, панди тут непотрібні.
спадник

Мені важко відтворити цей сюжет. Якими були б деякі (менші) зразкові значення для досягнення цього?
JRsz

21

Я щойно стикався з цією ж проблемою. Я рівномірно розподілені дані , які в 3 - 1-D масивів замість 2-D масивів, 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, спробуйте використовувати менший розмір сітки. Я майже впевнений, що саме це встановлює ширину між контурами. Оцінюючи по тоншій сітці, ви по суті повинні зменшити "розмір пікселів" і збільшити роздільну здатність, наближаючись до більш плавного градієнта.
Стівен К. Хоуелл

Чи є спосіб пофарбувати поверхню відповідно до конкретних категорій? Для екс. Категорія x, y, z - це формат даних, і я хотів би пофарбувати поверхню, що проходить через x, y, z відповідно до певної категорії.
Рудреш Айгаонкар

@RudreshAjgaonkar, ви повинні мати можливість використовувати три окремі команди сюжету, по одній для кожної вашої категорії, використовуючи будь-яке забарвлення для кожної з цих трьох.
Стівен К. Хоуелл

чи можете ви надати зразок коду, будь ласка? Я досить новачок у matplotlib та python.
Rudresh Ajgaonkar

4

Тільки щоб прозвучити, Емануїл мав відповідь, яку я (і, мабуть, багато інших) шукаю. Якщо у вас є 3D-розсіяні дані в 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) - схожий на каркас

Діапазон даних даних

Діапазон даних даних

3d розкидання даних

3d розкидання даних

Код виглядає так:

    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

ось пітонічний спосіб перетворити свої 3-кортежі в 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 триангуляція (інтерполяція), вона перетворює 1d x, y, z у щось, що відповідає (?):

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


Ні ... XYZ є двовимірним у цьому прикладі.
Вім

Я стою виправлений. Використовуйте meshgrid (), якщо ваші дані рівномірно розташовані, як у пов'язаному прикладі. Інтерполюйте, наприклад, з griddata (), якщо ваші дані, якщо вони не розташовані однаково.
Діма Тиснек

1

У Matlab я зробив що - щось подібне з допомогою delaunayфункції на x, yCoords тільки (НЕ z), а потім змова з trimeshабо trisurf, використовуючи в zякості висоти.

У SciPy є клас Delaunay , який базується на тій же базовій бібліотеці QHull, що і delaunayфункція Matlab , тому ви повинні отримати однакові результати.

Звідси повинно бути кілька рядків коду для перетворення цього полігону Плотінг в прикладі python-matplotlib у те, що ви хочете досягти, оскільки Delaunayдає специфікацію кожного трикутного багатокутника.


Дивіться цю відповідь на основі ax.plot_trisurf(..).
Євгеній Сергєєв

1

Просто додайте кілька додаткових думок, які можуть допомогти іншим із нерегулярними проблемами типу домену. У ситуації, коли у користувача є три вектори / списки, x, y, z, що представляють собою 2D-рішення, де z має бути нанесено на прямокутну сітку як поверхню, застосовуються коментарі ArtifixR 'plot_trisurf ()'. Аналогічний приклад, але з не прямокутним доменом:

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) де вони були видалені:

введіть тут опис зображення

трикутники вилучено

Я сподіваюсь, що вищесказане може допомогти людям із ситуацією увігнутості в даних рішення.


0

Неможливо безпосередньо зробити 3d поверхню за допомогою своїх даних. Я рекомендую вам побудувати модель інтерполяції, використовуючи такі інструменти, як pykridge . Процес включає три етапи:

  1. Навчіть модель інтерполяції, використовуючи pykridge
  2. Побудувати сітку з Xта Yвикористовуватиmeshgrid
  3. Інтерполяція значень для Z

Створивши свою сітку та відповідні Zзначення, тепер ви готові розпочати роботу plot_surface. Зауважте, що залежно від розміру ваших даних, meshgridфункція може працювати деякий час. Обхідний шлях полягає в створенні рівномірно розташованих зразків , використовуючи np.linspaceдля Xі Yосей, а потім застосувати інтерполяцію , щоб вивести необхідні Zзначення. Якщо так, то інтерпольовані значення можуть відрізнятися від оригіналу , Zтак як Xі Yзмінилися.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.