Перейти к содержимому

Как построить трехмерный график в python

  • автор:

The mplot3d toolkit#

This tutorial showcases various 3D plots. Click on the figures to see each full gallery example with the code that generates the figures.

3D Axes (of class Axes3D ) are created by passing the projection="3d" keyword argument to Figure.add_subplot :

Multiple 3D subplots can be added on the same figure, as for 2D subplots.

../../_images/sphx_glr_subplot3d_001.png

Changed in version 3.2.0: Prior to Matplotlib 3.2.0, it was necessary to explicitly import the mpl_toolkits.mplot3d module to make the ‘3d’ projection to Figure.add_subplot .

See the mplot3d FAQ for more information about the mplot3d toolkit.

Line plots#

See Axes3D.plot for API documentation.

../../_images/sphx_glr_lines3d_001.png

Scatter plots#

See Axes3D.scatter for API documentation.

../../_images/sphx_glr_scatter3d_001.png

Wireframe plots#

See Axes3D.plot_wireframe for API documentation.

../../_images/sphx_glr_wire3d_001.png

Surface plots#

See Axes3D.plot_surface for API documentation.

../../_images/sphx_glr_surface3d_001.png

Tri-Surface plots#

See Axes3D.plot_trisurf for API documentation.

../../_images/sphx_glr_trisurf3d_001.png

Contour plots#

See Axes3D.contour for API documentation.

../../_images/sphx_glr_contour3d_001.png

Filled contour plots#

See Axes3D.contourf for API documentation.

../../_images/sphx_glr_contourf3d_001.png

New in version 1.1.0: The feature demoed in the second contourf3d example was enabled as a result of a bugfix for version 1.1.0.

Многомерные графики в Python — от трёхмерных и до шестимерных

Визуализация — важная часть анализа данных, а способность посмотреть на несколько измерений одновременно эту задачу облегчает. В туториале мы будем рисовать графики вплоть до 6 измерений.

Plotly — это питоновская библиотека с открытым исходным кодом для разнообразной визуализации, которая предлагает гораздо больше настроек, чем известные matplotlib и seaborn. Модуль устанавливается как обычно — pip install plotly. Его мы и будем использовать для рисования графиков.

Давайте подготовим данные

Для визуализации мы используем простые данные об автомобилях от UCI (Калифорнийский университет в Ирвине — прим. перев.), которые представляют собой 26 характеристик для 205 машин (26 столбцов на 205 строк). Для визуализации шести измерений мы возьмём такие шесть параметров.

Загрузим данные из CSV с помощью pandas.

Теперь, подготовившись, начнем с двух измерений.

Двухмерная диаграмма рассеяния

Диаграмма рассеяния — весьма простой и распространенный график. Из 6 параметров, price и curb-weight используются ниже как Y и X соответственно.

В plotly процесс немного отличен от аналогичного в Matplotlib. Мы должны создать layout и figure, передав их в функцию offline.plot, после чего результат будет сохранён в HTML файл в текущей рабочей директории. Вот скриншот того, что получится. В конце статьи будет ссылка на GitHub репозиторий с готовыми интерактивными HTML-графиками.

Диаграмма рассеяния в 3D

Мы можем добавить третий параметр horsepower (количество лошадиных сил) на ось Z. Plotly предоставляет функцию Scatter3D для построения интерактивных трёхмерных графиков.

Вместо того чтобы вставлять код сюда каждый раз, я добавлял его в репозиторий.

(Удобнее всего смотреть релевантный код в соседней вкладке параллельно со чтением — прим. перев.)

Добавление четвёртого измерения

Мы знаем, что использовать больше трёх измерений напрямую нельзя, но есть обходной путь: мы можем эмулировать глубину для визуализации более высоких измерений с помощью цвета, размера или формы.

Здесь, наряду с тремя предыдущими характеристиками, мы будем использовать пробег в городских условиях — city-mpg как четвертое измерение, за которое будет отвечать параметр markercolor функции Scatter3D. Более светлый оттенок маркера будет означать меньший пробег.

Сразу же бросается в глаза, что чем выше цена, количество лошадей и масса, тем меньше будет пробег.

Добавление пятого измерения

Размер маркера можно использовать для визуализации 5-го измерения. Мы используем характеристику engine-size (размер двигателя) для параметра markersize функции Scatter3D.

Наблюдения: размер двигателя связан с некоторыми из предыдущих параметров. Чем выше цена, тем больше двигатель. Ра́вно как и: ниже пробег — больше двигатель.

Добавление шестого измерения

Форма маркера отлично подходит для визуализации категорий. Plotly даёт на выбор 10 различных фигур для 3D графика (звёздочка, круг, квадрат и т.д.). Таким образом, в качестве формы можно показать до 10 различных значений.

У нас есть характеристика num-of-doors, которая содержит целые числа — количество дверей (2 или 4). Преобразуем эти значения в фигуры: квадрат для 4 дверей, круг для 2 дверей. Используется параметр markersymbol функции Scatter3D.

Наблюдения: такое чувство, что у всех самых дешёвых машин по 4 двери (круги). Продолжая изучать график, можно будет сделать больше предположений и выводов.

6D график с формой маркера в качестве шестого измерения (количество дверей)

Можем ли мы добавить больше измерений?

Конечно можем! У маркеров есть больше свойств, таких как непрозрачность и градиенты, которые можно задействовать. Но чем больше измерений мы добавляем, тем труднее удержать их все в голове.

Исходный код

Код на Python и интерактивные графики для всех фигур доступны на GitHub здесь.

Использование библиотеки Matplotlib. Как рисовать трехмерные графики

В этой статье мы разберемся с тем, как можно рисовать трехмерные графики функции (поверхности), а также какие настройки внешнего вида существуют.

Кроме библиотеки Matplotlib нам понадобится математическая библиотека numpy, так как некоторые методы классов рисования трехмерных графиков в качестве параметров ожидают экземпляры класса numpy.array, да и сама библиотека Numpy позволяет значительно сократить количество строк кода.

Первый трехмерный график

Для того, чтобы нарисовать трехмерный график, в первую очередь надо создать трехмерные оси. Для этого выполним короткий пример:

import matplotlib. pyplot as plt

if __name__ == ‘__main__’ :
fig = plt. figure ( )
axes = fig. add_subplot ( projection = ‘3d’ )

Чтобы создать трехмерные оси, сначала мы создаем с помощью функции figure() экземпляр класса matplotlib.figure.Figure. Затем у экземпляра этого класса вызывается метод add_subplot(), который может принимать различное количество параметров, но в данном случае мы передаем один именованный параметр — projection, который описывает тип осей. Для создания трехмерных осей значение параметра projection должно быть строкой «3d». В результате мы получим экземпляр класса осей, с которым мы будем работать в дальнейшем. Этот экземпляр принадлежит к классу, производному от Axes3D (строго говоря он имеет класс matplotlib.axes._subplots.Axes3DSubplot, но этот класс создается динамически в недрах библиотеки, поэтому его нет в документации). Через экземпляр этого класса мы будем рисовать графики и настраивать его внешний вид.

В результате выполнения приведенного выше скрипта мы увидим следующее окно с трехмерными осями, но пока без графика:

Полученные оси можно вращать мышкой.

Пришло время нарисовать что-то трехмерное в полученных осях. Для всех примеров мы будем использовать следующую функцию, от двух координат.

Для начала нужно подготовить данные для рисования. Нам понадобятся три двумерные матрицы: матрицы X и Y будут хранить координаты сетки точек, в которых будет вычисляться приведенная выше функция, а матрица Z будет хранить значения этой функции в соответствующей точке.

Сначала нужно подготовить прямоугольную сетку на плоскости XY, в узлах которой будут рассчитываться значения отображаемой функции (значение по оси Z). Для создания такой сетки можно воспользоваться функцией numpy.meshgrid(). Эта функция, в простейшем случае, принимает несколько одномерных массивов. Нам нужно два массива для осей X и Y, которые содержат значения координат узлов вдоль соответствующей оси, и могут иметь разный размер. Эта функция возвратит в нашем случае две двумерные матрицы, описывающие координаты X и Y на двумерной сетке. Первая матрица будет создана размножением первого переданного параметра в функцию numpy.meshgrid() вдоль строк первой возвращаемой матрицы, а вторая матрица создается размножением второго переданного одномерного массива вдоль столбцов второй матрицы.

Работа этой функции показана в документации к numpy на простом примере:

Теперь по индексу узла сетки мы можем узнать реальные координаты: X[0][0] = 1, Y[0][0] = 4 и т.п. Такие двумерные матрицы позволяют очень легко рассчитывать значения функций от двух аргументов, используя поэлементные операции (векторизацию), это будет показано в следующем блоке кода. И кроме того, эти же матрицы требуются для рисования трехмерных поверхностей в Matplotlib.

Чтобы отделить подготовку данных от самого рисования, создание сетки и расчет функции выделим в отдельную функцию:

def makeData ( ) :
# Строим сетку в интервале от -10 до 10, имеющую 100 отсчетов по обоим координатам
x = numpy. linspace ( — 10 , 10 , 100 )
y = numpy. linspace ( — 10 , 10 , 100 )

# Создаем двумерную матрицу-сетку
xgrid , ygrid = numpy. meshgrid ( x , y )

# В узлах рассчитываем значение функции
z = numpy. sin ( xgrid ) * numpy. sin ( ygrid ) / ( xgrid * ygrid )
return xgrid , ygrid , z

Эта функция возвращает три двумерные матрицы: x, y, z. Координаты x и y лежат на отрезке от -10 до 10 с шагом 0.1. Для задания интервала по осям X и Y мы воспользовались функцией numpy.linspace(), которая создает одномерный массив со значениями из заданного интервала [-10; 10] с указанным количеством отсчетов в нет (в нашем случае — 100). По умолчанию правый конец интервала также включается в результат (если правый конец не нужно включать в результат, то можно передать параметр endpoint, равный False).

В данном примере для простоты по осям берется четное количество отсчетов, благодаря этому особая точка с координатами (0, 0) не попадает в создаваемую сетку, поэтому дополнительная проверка не проводится. Строго говоря, именно для данной аналитической функции можно было бы воспользоваться функцией numpy.sinc(). Поскольку вычисляемая функция выбрана просто для примера, не стоит акцентировать внимание на ее математической записи.

Теперь возвращаемся непосредственно к рисованию. Чтобы отобразить наши данные, достаточно вызвать метод plot_surface() экземпляра класса Axes3D, в который передадим полученные с помощью функции makeData() двумерные матрицы.

Теперь наш пример выглядит следующим образом:

import matplotlib. pyplot as plt

def makeData ( ) :
# Строим сетку в интервале от -10 до 10, имеющую 100 отсчетов по обоим координатам
x = numpy. linspace ( — 10 , 10 , 100 )
y = numpy. linspace ( — 10 , 10 , 100 )

# Создаем двумерную матрицу-сетку
xgrid , ygrid = numpy. meshgrid ( x , y )

# В узлах рассчитываем значение функции
z = numpy. sin ( xgrid ) * numpy. sin ( ygrid ) / ( xgrid * ygrid )
return xgrid , ygrid , z

if __name__ == ‘__main__’ :
x , y , z = makeData ( )

fig = plt. figure ( )
axes = fig. add_subplot ( projection = ‘3d’ )

# .
axes. plot_surface ( x , y , z )

Если мы запустим этот скрипт, то появится окно с такой трехмерной поверхностью:

Полученную штуку можно вращать с помощью мышки. Matplotlib не использует графический ускоритель, поэтому вращение происходит довольно медленно, хотя скорость зависит от количества точек на поверхности.

Кроме перечисленных параметров метод plot_surface() имеет дополнительные параметры, которые мы сейчас и рассмотрим.

Изменяем внешний вид графика

Шаг сетки

По умолчанию метод plot_surface() использует разрежение исходных данных, чтобы ускорить отображение. Регулировать степень разрежения (или отключить его) можно с помощью пар именованных параметров, передаваемых в метод plot_surface(). С помощью параметров rcount и ccount можно задать количество отсчетов по двум осям (по строкам и по столбцам в исходных данных соответственно). По умолчанию используются значения rcount = ccount = 50. Давайте это проверим. Для этого изменим в предыдущем примере строку с использованием метода plot_surface() следующим образом:

При этом сетка на графике не изменится и будет, как в предыдущем примере:

Теперь сделаем сетку более мелкой, зададим количество отсчетов по обеим осям равное 100 (как раз столько, сколько у нас есть отсчетов в исходных данных):

Результат показан на следующем рисунке:

Или сделаем сетку более редкой, изменив вызов метода plot_surface() на следующий:

Результат будет выглядеть следующим образом:

Аналогичных результатов можно добиться, используя пары параметров rstride и cstride. Но если параметры rcount и ccount задавали количество отсчетов по осям, то rstride и cstride задают степень прореженности (децимации) по осям, то есть сколько отсчетом надо пропустить.

Как мы говорили в начале, используется сетка с 50 точками по каждой оси, а поскольку у нас в данных по 100 отсчетов, то это равносильно для наших данных значениям rstride = cstride = 2. Давайте это проверим, изменив вызов метода plot_surface() на следующий:

В результате получим следующий график:

Он не отличается от графика, полученного с параметрами по умолчанию.

Отключим прореживаение, установив параметры rstride = cstride = 1:

Сетка станет более мелкой:

Или наоборот уменьшим количество данных в сетке, увеличив параметры rstride и cstride:

Результат будет выглядеть следующим образом:

Таким образом у нас есть два инструмента для изменения прореживания данных.

Изменение цвета

Теперь изменим цвет поверхности с помощью параметра color. Этот параметр представляет собой строку, которая описывает цвет. Строка цвета может задаваться разными способами:

Цвет можно определить английским словом для соответствующего цвета или одной буквой. Таких цветов не много:

  • ‘b’ или ‘blue’
  • ‘g’ или ‘green’
  • ‘r’ или ‘red’
  • ‘c’ или ‘cyan’
  • ‘m’ или ‘magenta’
  • ‘y’ или ‘yellow’
  • ‘k’ или ‘black’
  • ‘w’ или ‘white’

Для примера сделаем поверхность желтой:

Того же самого результата мы могли бы добиться, используя следующую строку:

Если нам нужен серый цвет, то его яркость мы можем задать с помощью строки, содержащей число в интервале от 0.0 до 1.0 (0 — белый, 1 — черный). Например, мы можем написать следующую строку:

В этом случае мы увидим такой вот серый график:

Кроме того мы можем задавать цвет так, как это принято в HTML после символа решетки (‘#’). Например, можем задать цвет следующим образом:

Тогда график позеленеет:

Использование цветовых карт (colormap)

Цветовые карты используются, если нужно указать в какие цвета должны окрашиваться участки трехмерной поверхности в зависимости от значения Z в этой области (задание цветового градиента). Тема использование градиентов сама по себе большая и интересная, но мы сейчас рассмотрим только некоторые ее аспекты. Чтобы при выводе графика использовался градиент, в качестве значения параметра cmap (от слова colormap — цветовая карта) нужно передать экземпляр класса matplotlib.colors.Colormap или производного от него.

Следующий пример использует класс LinearSegmentedColormap, производный от Colormap, чтобы создать градиент перехода от синего цвета к красному через белый.

import matplotlib. pyplot as plt

# .
from matplotlib. colors import LinearSegmentedColormap

def makeData ( ) :
# Строим сетку в интервале от -10 до 10, имеющую 100 отсчетов по обоим координатам
x = numpy. linspace ( — 10 , 10 , 100 )
y = numpy. linspace ( — 10 , 10 , 100 )

# Создаем двумерную матрицу-сетку
xgrid , ygrid = numpy. meshgrid ( x , y )

# В узлах рассчитываем значение функции
z = numpy. sin ( xgrid ) * numpy. sin ( ygrid ) / ( xgrid * ygrid )
return xgrid , ygrid , z

if __name__ == ‘__main__’ :
x , y , z = makeData ( )

fig = plt. figure ( )
axes = fig. add_subplot ( projection = ‘3d’ )

# .
cmap = LinearSegmentedColormap. from_list ( ‘red_blue’ , [ ‘b’ , ‘w’ , ‘r’ ] , 256 )
axes. plot_surface ( x , y , z , color = ‘#11aa55’ , cmap = cmap )

Здесь для создания цветовой карты используется статический метод from_list(), который принимает три параметра:

  • Имя создаваемой карты
  • Список цветов, начиная с цвета для минимального значения на графике (голубой — ‘b’), через промежуточные цвета (у нас это белый — ‘w’) к цвету для максимального значения функции (красный — ‘r’).
  • Количество цветовых переходов. Чем это число больше, тем более плавный градиент, но тем больше памяти он занимает.

Результат выполнения этого скрипта выглядит следующим образом:

Если вы не хотите каждый раз создавать свою цветовую карту, то можете воспользоваться одной из уже готовых карт, которые располагаются в модуле matplotlib.cm.

Чтобы узнать какие цветовые карты существуют, можно воспользоваться следующим скриптом:

from matplotlib import cm

Например, в Matplotlib 3.4.3 вывелись следующие цветовые карты:

[‘Accent’, ‘Accent_r’, ‘Blues’, ‘Blues_r’, ‘BrBG’, ‘BrBG_r’, ‘BuGn’, ‘BuGn_r’, ‘BuPu’, ‘BuPu_r’, ‘CMRmap’, ‘CMRmap_r’, ‘Dark2’, ‘Dark2_r’, ‘GnBu’, ‘GnBu_r’, ‘Greens’, ‘Greens_r’, ‘Greys’, ‘Greys_r’, ‘LUTSIZE’, ‘MutableMapping’, ‘OrRd’, ‘OrRd_r’, ‘Oranges’, ‘Oranges_r’, ‘PRGn’, ‘PRGn_r’, ‘Paired’, ‘Paired_r’, ‘Pastel1’, ‘Pastel1_r’, ‘Pastel2’, ‘Pastel2_r’, ‘PiYG’, ‘PiYG_r’, ‘PuBu’, ‘PuBuGn’, ‘PuBuGn_r’, ‘PuBu_r’, ‘PuOr’, ‘PuOr_r’, ‘PuRd’, ‘PuRd_r’, ‘Purples’, ‘Purples_r’, ‘RdBu’, ‘RdBu_r’, ‘RdGy’, ‘RdGy_r’, ‘RdPu’, ‘RdPu_r’, ‘RdYlBu’, ‘RdYlBu_r’, ‘RdYlGn’, ‘RdYlGn_r’, ‘Reds’, ‘Reds_r’, ‘ScalarMappable’, ‘Set1’, ‘Set1_r’, ‘Set2’, ‘Set2_r’, ‘Set3’, ‘Set3_r’, ‘Spectral’, ‘Spectral_r’, ‘Wistia’, ‘Wistia_r’, ‘YlGn’, ‘YlGnBu’, ‘YlGnBu_r’, ‘YlGn_r’, ‘YlOrBr’, ‘YlOrBr_r’, ‘YlOrRd’, ‘YlOrRd_r’, DeprecatedCmapDictWrapper’, ‘__builtin_cmaps’, ‘__builtins_, _cached_, _doc_, _file_, _loader_, _name_, _package_, _spec_, ‘_api’, ‘_cmap_registry’, ‘_gen_cmap_registry’, ‘afmhot’, ‘afmhot_r’, ‘autumn’, ‘autumn_r’, ‘binary’, ‘binary_r’, ‘bone’, ‘bone_r’, ‘brg’, ‘brg_r’, ‘bwr’, ‘bwr_r’, ‘cbook’, ‘cividis’, ‘cividis_r’, ‘cmap_d’, ‘cmaps_listed’, ‘colors’, ‘cool’, ‘cool_r’, ‘coolwarm’, ‘coolwarm_r’, ‘copper’, ‘copper_r’, ‘cubehelix’, ‘cubehelix_r’, ‘datad’, ‘flag’, ‘flag_r’, ‘get_cmap’, ‘gist_earth’, ‘gist_earth_r’, ‘gist_gray’, ‘gist_gray_r’, ‘gist_heat’, ‘gist_heat_r’, ‘gist_ncar’, ‘gist_ncar_r’, ‘gist_rainbow’, ‘gist_rainbow_r’, ‘gist_stern’, ‘gist_stern_r’, ‘gist_yarg’, ‘gist_yarg_r’, ‘gnuplot’, ‘gnuplot2’, ‘gnuplot2_r’, ‘gnuplot_r’, ‘gray’, ‘gray_r’, ‘hot’, ‘hot_r’, ‘hsv’, ‘hsv_r’, ‘inferno’, ‘inferno_r’, ‘jet’, ‘jet_r’, ‘ma’, ‘magma’, ‘magma_r’, ‘mpl’, ‘nipy_spectral’, ‘nipy_spectral_r’, ‘np’, ‘ocean’, ‘ocean_r’, ‘pink’, ‘pink_r’, ‘plasma’, ‘plasma_r’, ‘prism’, ‘prism_r’, ‘rainbow’, ‘rainbow_r’, ‘register_cmap’, ‘seismic’, ‘seismic_r’, ‘spring’, ‘spring_r’, ‘summer’, ‘summer_r’, ‘tab10’, ‘tab10_r’, ‘tab20’, ‘tab20_r’, ‘tab20b’, ‘tab20b_r’, ‘tab20c’, ‘tab20c_r’, ‘terrain’, ‘terrain_r’, ‘turbo’, ‘turbo_r’, ‘twilight’, ‘twilight_r’, ‘twilight_shifted’, ‘twilight_shifted_r’, ‘unregister_cmap’, ‘viridis’, ‘viridis_r’, ‘winter’, ‘winter_r’]

Как эти цветовые карты выглядят, можно увидеть на этой странице документации.

Воспользуемся некоторыми из них. Следующий пример строит два трехмерных графика в одном окне. Для левого графика цветовая карта jet, а для правого — Spectral.

# .
from matplotlib. colors import LightSource
import matplotlib. pyplot as plt

def makeData ( ) :
# Строим сетку в интервале от -10 до 10, имеющую 100 отсчетов по обоим координатам
x = numpy. linspace ( — 10 , 10 , 100 )
y = numpy. linspace ( — 10 , 10 , 100 )

# Создаем двумерную матрицу-сетку
xgrid , ygrid = numpy. meshgrid ( x , y )

# В узлах рассчитываем значение функции
zgrid = numpy. sin ( xgrid ) * numpy. sin ( ygrid ) / ( xgrid * ygrid )
return xgrid , ygrid , zgrid

if __name__ == ‘__main__’ :
x , y , z = makeData ( )

fig = plt. figure ( figsize = ( 10 , 8 ) )
axes_1 = fig. add_subplot ( 1 , 2 , 1 , projection = ‘3d’ )
axes_2 = fig. add_subplot ( 1 , 2 , 2 , projection = ‘3d’ )

# .
axes_1. plot_surface ( x , y , z , cmap = ‘jet’ )
axes_1. set_title ( "cmap=’jet’" )

# .
axes_2. plot_surface ( x , y , z , cmap = ‘Spectral’ )
axes_2. set_title ( "cmap=’Spectral’" )

В результате будут показаны следующие трехмерные графики:

Настройка цвета и толщины линий

Иногда бывает полезно для наглядности сделать более выделяющимися линии сетки трехмерной поверхности. Цвет линий в методе plot_surface() можно задавать с помощью именованного параметра edgecolors, а толщину линий — с помощью параметра linewidth.

Следующий пример делает линии сетки более жирными и черными. Для наглядности сетка в этом примере сделана более редкой с помощью обсуждавшихся выше параметров rcount и ccount, а для раскраски поверхности с помощью параметра cmap задается цветовая карта с именем «jet».

import matplotlib. pyplot as plt

def makeData ( ) :
# Строим сетку в интервале от -10 до 10, имеющую 100 отсчетов по обоим координатам
x = numpy. linspace ( — 10 , 10 , 100 )
y = numpy. linspace ( — 10 , 10 , 100 )

# Создаем двумерную матрицу-сетку
xgrid , ygrid = numpy. meshgrid ( x , y )

# В узлах рассчитываем значение функции
z = numpy. sin ( xgrid ) * numpy. sin ( ygrid ) / ( xgrid * ygrid )
return xgrid , ygrid , z

if __name__ == ‘__main__’ :
x , y , z = makeData ( )

fig = plt. figure ( )
axes = fig. add_subplot ( projection = ‘3d’ )

# .
axes. plot_surface ( x , y , z , rcount = 40 , ccount = 40 , cmap = ‘jet’ ,
linewidth = 0.5 , edgecolors = ‘k’ )

Результат показан на следующем рисунке:

Установка точки наблюдения

Хотя трехмерный график можно вращать с помощью мышки, но также есть возможность установить точку наблюдения, которая будет установлена по умолчанию. По умолчанию точка наблюдения установлена в точке, которая имеет угловые координаты: азимут (azimuth) равен -60°, а угол места (elevation) равен 30°. Для изменения положения точки наблюдения предназначен метод view_init() из класса Axes3D. Этот метод принимает два параметра: elev — угол места и azim — азимут. Оба угла задаются в градусах. Каждый из этих параметров может принимать значение None, в этом случае данная угловая координата будет изменена на значение по умолчанию.

Следующий пример устанавливает значение азимута, равным 0°, а угол места равным 45°. Для большей наглядности оформление графика взято из предыдущего примера, а также добавлены имена осей с помощью методов set_xlabel() и set_ylabel().

import matplotlib. pyplot as plt

def makeData ( ) :
# Строим сетку в интервале от -10 до 10, имеющую 100 отсчетов по обоим координатам
x = numpy. linspace ( — 10 , 10 , 100 )
y = numpy. linspace ( — 10 , 10 , 100 )

# Создаем двумерную матрицу-сетку
xgrid , ygrid = numpy. meshgrid ( x , y )

# В узлах рассчитываем значение функции
z = numpy. sin ( xgrid ) * numpy. sin ( ygrid ) / ( xgrid * ygrid )
return xgrid , ygrid , z

if __name__ == ‘__main__’ :
x , y , z = makeData ( )

fig = plt. figure ( )
axes = fig. add_subplot ( projection = ‘3d’ )

# .
axes. plot_surface ( x , y , z , cmap = ‘jet’ , rcount = 40 , ccount = 40 ,
linewidth = 0.5 , edgecolors = ‘k’ )
axes. view_init ( elev = 45 , azim = 0 )
axes. set_xlabel ( ‘X’ )
axes. set_ylabel ( ‘Y’ )

Результат выполнения этого скрипта показан на следующем рисунке:

Также координаты точки наблюдения можно указать при создании осей, передав одноименные параметры в метод add_subplot(), что показано в следующем примере:

import matplotlib. pyplot as plt

def makeData ( ) :
# Строим сетку в интервале от -10 до 10, имеющую 100 отсчетов по обоим координатам
x = numpy. linspace ( — 10 , 10 , 100 )
y = numpy. linspace ( — 10 , 10 , 100 )

# Создаем двумерную матрицу-сетку
xgrid , ygrid = numpy. meshgrid ( x , y )

# В узлах рассчитываем значение функции
z = numpy. sin ( xgrid ) * numpy. sin ( ygrid ) / ( xgrid * ygrid )
return xgrid , ygrid , z

if __name__ == ‘__main__’ :
x , y , z = makeData ( )

# .
axes = fig. add_subplot ( projection = ‘3d’ , elev = 45 , azim = 0 )

# .
axes. plot_surface ( x , y , z , cmap = ‘jet’ , rcount = 40 , ccount = 40 ,
linewidth = 0.5 , edgecolors = ‘k’ )
axes. set_xlabel ( ‘X’ )
axes. set_ylabel ( ‘Y’ )

Внешний вид графика в этом случае будет точно таким же, как и в предыдущем примере

Надо сказать, что в в данном методе координаты азимут и угол места отсчитываются не совсем так, как это принято в классических системах координат. Азимут отсчитывается не от оси X, которая начинается в начале координат, в сторону оси Y, а от линии, параллельной оси X, которая исходит из центра трехмерной фигуры, этот угол также отсчитывается в сторону оси Y. А угол места отсчитывается не от плоскости OXY, которая проходит через плоскость Z = 0, а от нижней плоскости осей для данной трехмерной фигуры. Чтобы это показать, в следующем примере отображаемая функция смещена по осям Y и Z, но при этом азимут и угол места заданы, как в предыдущем примере. То есть система координат для положения точки наблюдения привязана не к началу системы координат графика, а к отображаемой области, что в общем-то логично с точки зрения пользователя.

import matplotlib. pyplot as plt

def makeData ( ) :
# Строим сетку в интервале от -10 до 10, имеющую 100 отсчетов по обоим координатам
x = numpy. linspace ( — 10 , 10 , 100 )
y = numpy. linspace ( — 5 , 15 , 100 )

# Создаем двумерную матрицу-сетку
xgrid , ygrid = numpy. meshgrid ( x , y )

# В узлах рассчитываем значение функции
z = numpy. sin ( xgrid ) * numpy. sin ( ygrid — 5 ) / ( xgrid * ( ygrid — 5 ) ) — 5
return xgrid , ygrid , z

if __name__ == ‘__main__’ :
x , y , z = makeData ( )

fig = plt. figure ( )
# .
axes = fig. add_subplot ( projection = ‘3d’ , elev = 45 , azim = 0 )

# .
axes. plot_surface ( x , y , z , cmap = ‘jet’ , rcount = 40 , ccount = 40 ,
linewidth = 0.5 , edgecolors = ‘k’ )
axes. set_xlabel ( ‘X’ )
axes. set_ylabel ( ‘Y’ )

Результат выглядит следующим образом:

Источник света

Для большей натуралистичности и визуальной привлекательности при построении трехмерных поверхностей используется неравномерное освещение трехмерных объектов (затенение). Если нужно сделать отображаемый график более «плоским», то затенение можно отключить, и тогда освещенность всех элементов трехмерной поверхности будет одинаковая. Для этого в метод plot_surface() нужно передать именованный параметр shade, равный значению False. Затенение автоматически отключается, если используется цветовая карта, которая устанавливается с помощью параметра cmap.

Следующий пример отображает два трехмерных графика в одном окне, чтобы можно было увидеть разницу между включенным и отключенным затенением.

import matplotlib. pyplot as plt

def makeData ( ) :
# Строим сетку в интервале от -10 до 10, имеющую 100 отсчетов по обоим координатам
x = numpy. linspace ( — 10 , 10 , 100 )
y = numpy. linspace ( — 10 , 10 , 100 )

# Создаем двумерную матрицу-сетку
xgrid , ygrid = numpy. meshgrid ( x , y )

# В узлах рассчитываем значение функции
z = numpy. sin ( xgrid ) * numpy. sin ( ygrid ) / ( xgrid * ygrid )
return xgrid , ygrid , z

if __name__ == ‘__main__’ :
x , y , z = makeData ( )

fig = plt. figure ( figsize = ( 10 , 8 ) )
axes_shade = fig. add_subplot ( 1 , 2 , 1 , projection = ‘3d’ )
axes_no_shade = fig. add_subplot ( 1 , 2 , 2 , projection = ‘3d’ )

axes_shade. plot_surface ( x , y , z , rcount = 40 , ccount = 40 , shade = True )
axes_shade. set_title ( "shade=True (default)" )

axes_no_shade. plot_surface ( x , y , z , rcount = 40 , ccount = 40 , shade = False )
axes_no_shade. set_title ( "shade=False" )

Результат выглядит следующим образом:

Но если сделать более выделяющуюся сетку, как было описано в разделе Настройка цвета и толщины линий, то визуально разница будет уже не столь существенная:

import matplotlib. pyplot as plt

def makeData ( ) :
# Строим сетку в интервале от -10 до 10, имеющую 100 отсчетов по обоим координатам
x = numpy. linspace ( — 10 , 10 , 100 )
y = numpy. linspace ( — 10 , 10 , 100 )

# Создаем двумерную матрицу-сетку
xgrid , ygrid = numpy. meshgrid ( x , y )

# В узлах рассчитываем значение функции
z = numpy. sin ( xgrid ) * numpy. sin ( ygrid ) / ( xgrid * ygrid )
return xgrid , ygrid , z

if __name__ == ‘__main__’ :
x , y , z = makeData ( )

fig = plt. figure ( figsize = ( 10 , 8 ) )
axes_shade = fig. add_subplot ( 1 , 2 , 1 , projection = ‘3d’ )
axes_no_shade = fig. add_subplot ( 1 , 2 , 2 , projection = ‘3d’ )

axes_shade. plot_surface ( x , y , z , rcount = 40 , ccount = 40 ,
linewidth = 0.5 , edgecolors = ‘k’ , shade = True )
axes_shade. set_title ( "shade=True (default)" )

axes_no_shade. plot_surface ( x , y , z , rcount = 40 , ccount = 40 ,
linewidth = 0.5 , edgecolors = ‘k’ , shade = False )
axes_no_shade. set_title ( "shade=False" )

Если используется затенение, то можно установить источник света таким образом, чтобы он освещал поверхность с определенного направления. Чтобы установить источник света, в метод plot_surface() нужно передать параметр lightsource, который должен быть экземпляром класса matplotlib.colors.LightSource. Конструктор этого класса принимает два основных параметра (на самом деле больше, но остальные параметры оставлены только для совместимости со старыми версиями библиотеки): азимут (azdeg) и угол места (altdeg). Все углы задаются в градусах.

Следующий пример показывает разницу в отображении графиков при различных положениях источника света:

# .
from matplotlib. colors import LightSource
import matplotlib. pyplot as plt

def makeData ( ) :
# Строим сетку в интервале от -10 до 10, имеющую 100 отсчетов по обоим координатам
x = numpy. linspace ( — 10 , 10 , 100 )
y = numpy. linspace ( — 10 , 10 , 100 )

# Создаем двумерную матрицу-сетку
xgrid , ygrid = numpy. meshgrid ( x , y )

# В узлах рассчитываем значение функции
z = numpy. sin ( xgrid ) * numpy. sin ( ygrid ) / ( xgrid * ygrid )
return xgrid , ygrid , z

if __name__ == ‘__main__’ :
x , y , z = makeData ( )

fig = plt. figure ( figsize = ( 10 , 8 ) )
axes_1 = fig. add_subplot ( 1 , 2 , 1 , projection = ‘3d’ )
axes_2 = fig. add_subplot ( 1 , 2 , 2 , projection = ‘3d’ )

light_1 = LightSource ( azdeg = 300 , altdeg = 45 )
light_2 = LightSource ( azdeg = 60 , altdeg = 15 )

axes_1. plot_surface ( x , y , z , rcount = 40 , ccount = 40 ,
lightsource = light_1 )
axes_1. set_title ( ‘azdeg=300, altdeg=45’ )

axes_2. plot_surface ( x , y , z , rcount = 40 , ccount = 40 ,
lightsource = light_2 )
axes_2. set_title ( ‘azdeg=60, altdeg=15’ )

Результат выглядит следующим образом:

Кроме рисования трехмерных поверхностей в Matplotlib существует возможность рисования точек и линий в трехмерном пространстве, трехмерных гистограмм и плоских фигур в трехмерном пространстве. Но это уже темы для отдельных статей.

Вы можете подписаться на новости сайта через RSS, Группу Вконтакте или Канал в Telegram.

Three-Dimensional Plotting in Matplotlib

Matplotlib was initially designed with only two-dimensional plotting in mind. Around the time of the 1.0 release, some three-dimensional plotting utilities were built on top of Matplotlib’s two-dimensional display, and the result is a convenient (if somewhat limited) set of tools for three-dimensional data visualization. three-dimensional plots are enabled by importing the mplot3d toolkit, included with the main Matplotlib installation:

Once this submodule is imported, a three-dimensional axes can be created by passing the keyword projection=’3d’ to any of the normal axes creation routines:

With this three-dimensional axes enabled, we can now plot a variety of three-dimensional plot types. Three-dimensional plotting is one of the functionalities that benefits immensely from viewing figures interactively rather than statically in the notebook; recall that to use interactive figures, you can use %matplotlib notebook rather than %matplotlib inline when running this code.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *