земельные участки в матплотлиб - PullRequest
85 голосов
/ 07 февраля 2012

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

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

Ответы [ 7 ]

100 голосов
/ 07 февраля 2012

Для поверхностей это немного отличается от списка из 3 кортежей, вы должны передать сетку для домена в 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

20 голосов
/ 22 сентября 2017

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

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)

surface

Секция бонусов

Мне было интересно, как создавать интерактивные графики, в данном случае с искусственными данными

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
18 голосов
/ 30 мая 2015

Я только что столкнулся с этой же проблемой.У меня есть равномерно распределенные данные, которые находятся в 3-х мерных массивах вместо 2-х мерных, которые нужны matplotlib 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-х 1-мерных массивов.

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

Вот результирующие цифры:

enter image description here enter image description here

3 голосов
/ 07 февраля 2012

проверьте официальный пример.X, Y и Z действительно являются 2d массивами, numpy.meshgrid () - это простой способ получить 2d x, y сетку из значений 1d x и y.

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

здесьpythonic способ конвертировать ваши 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)

Вот триангуляция Метаплотлиба Делоне (интерполяция), она конвертирует 1d x, y, z в нечто совместимое (?):

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

2 голосов
/ 02 апреля 2018

Просто чтобы присоединиться, у Эмануэля был ответ, который я (и, вероятно, многие другие) ищу. Если у вас есть 3-мерные данные в 3 отдельных массивах, pandas - невероятная помощь и работает намного лучше, чем другие варианты. Для уточнения, предположим, что ваши x, y, z - некоторые произвольные переменные. В моем случае это были c, gamma и ошибки, потому что я тестировал машину опорных векторов. Существует много потенциальных вариантов построения данных:

  • scatter3D (cParams, gammas, avg_errors_array) - это работает, но слишком упрощенно
  • plot_wireframe (cParams, gammas, avg_errors_array) - это работает, но будет выглядеть некрасиво, если ваши данные не будут правильно отсортированы, как это может быть в случае с массивными кусками реальных научных данных
  • ax.plot3D (cParams, gammas, avg_errors_array) - аналогично каркасу

Каркасный график данных

Wireframe plot of the data

3d разброс данных

3d scatter of the data

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

    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 of xyz data

1 голос
/ 15 октября 2014

В Matlab я сделал нечто подобное, используя функцию delaunay только для координат x, y (не z), затем нанося на график trimesh или trisurf, используя z как высота.

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

Оттуда должно быть несколько строк кода для преобразования этого построения 3D-полигонов в примере python-matplotlib в то, что вы хотите достичь, так как Delaunay дает вам спецификацию каждого треугольного многоугольника .

0 голосов
/ 19 сентября 2018

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...