участки поверхности в matplotlib


105

У меня есть список из трех кортежей, представляющих набор точек в трехмерном пространстве. Я хочу построить поверхность, покрывающую все эти точки.

plot_surfaceФункция в mplot3dпакете требует в качестве аргументов X, Y и Z , чтобы быть 2d массивов. Подходит ли plot_surfaceфункция для построения поверхности и как мне преобразовать данные в требуемый формат?

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


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

Ответы:


121

Для поверхностей это немного отличается от списка из трех кортежей, вы должны передать сетку для домена в 2d массивах.

Если все, что у вас есть, это список трехмерных точек, а не какая-то функция f(x, y) -> z, тогда у вас возникнет проблема, потому что есть несколько способов триангулировать это трехмерное облако точек на поверхность.

Вот пример гладкой поверхности:

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.
Грегори Кун

16
Но что делать, если z - независимая переменная, а не функция от x и y?
Лабиба

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 импортировать Axes3D, а Axes3D нигде в приведенном выше коде не используется?
絢 瀬 絵 里

5
У этого импорта есть побочные эффекты. Использование kwarg projection='3d'в вызове fig.add_subplotбудет недоступно без этого импорта.
wim

34

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

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-D массивов, matplotlib«S plot_surfaceхочет. Мои данные оказались в виде, pandas.DataFrameпоэтому вот matplotlib.plot_surfaceпример с модификациями для построения 3 одномерных массивов.

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-х одномерных массивов.

# ~~~~ 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.
Рудреш Айгаонкар

4

Просто чтобы поддержать разговор, у Эмануэля был ответ, который я (и, вероятно, многие другие) ищу. Если у вас есть трехмерные разбросанные данные в 3 отдельных массивах, pandas будет невероятным подспорьем и работает намного лучше, чем другие варианты. Чтобы уточнить, предположим, что ваши 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 из 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 в этом примере двумерны.
wim

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

2

Просто чтобы добавить некоторые дополнительные мысли, которые могут помочь другим с проблемами нестандартного типа домена. Для ситуации, когда у пользователя есть три вектора / списка, x, y, z, представляющие 2D-решение, где 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 in python-matplotlib в то, что вы хотите достичь, так как Delaunayдает вам спецификацию каждого треугольного многоугольника.


См. Этот ответ на основе ax.plot_trisurf(..).
Евгений Сергеев

0

Невозможно напрямую создать трехмерную поверхность, используя ваши данные. Я бы порекомендовал вам построить модель интерполяции, используя некоторые инструменты, такие как pykridge . Процесс будет включать три этапа:

  1. Обучите модель интерполяции, используя pykridge
  2. Создайте сетку из Xи Yиспользуяmeshgrid
  3. Интерполировать значения для Z

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.