|
ВведениеВ Matplotlib версии 0.99 наконец-то появилась возможность рисовать кроме двумерных также и трехмерные графики. Лично для меня особенно не хватало аналога функции surf из Matlab. В этой статье мы разберемся с тем как можно рисовать трехмерные графики функции (поверхности) и какие настройки внешнего вида существуют. Для запуска примеров нам понадобится версия Matplotlib не ниже 0.99, скачать которую можно здесь. При написании этой статьи я использовал версию 0.99.1. Кроме того нам понадобится математическая библиотека numpy, так как некоторые методы классов рисования трехмерных графиков в качестве параметров ожидают экземпляры класса numpy.array, да и сама библиотека Numpy позволяет значительно сократить количество строк кода. Первый трехмерный графикВсе классы для работы с трехмерными графиками находятся в пакете mpl_toolkits.mplot3d, из которого нужно будет их импортировать. Для того, чтобы нарисовать трехмерный график, в первую очередь надо создать трехмерные оси. Чтобы их создать, нужно создать экземпляр класса mpl_toolkits.mplot3d.Axes3D, конструктор которого ожидает как минимум один параметр - экземпляр класса matplotlib.figure.Figure, который, в свою очередь, можно создать вызовом pylab.figure(). У конструктора класса matplotlib.figure.Figure есть еще и другие необязательные параметры, но пока мы их использовать не будем. Давайте нарисуем пустые оси: import pylab from mpl_toolkits.mplot3d import Axes3D fig = pylab.figure() Axes3D(fig) pylab.show() В результате мы увидим следующее окно: ![]() Полученные оси вы можете вращать мышкой. Пришло время нарисовать что-то трехмерное в полученных осях. Для всех примеров мы будем использовать следующую функцию, от двух координат. ![]() Для начала нужно подготовить данные для рисования. Нам понадобятся три двумерные матрицы: матрицы X и Y будут хранить координаты сетки точек, в которых будет вычисляться приведенная выше функция, а матрица Z будет хранить значения этой функции в соответствующей точке. Если мы хотим нарисовать трехмерный график на эквидистантной сетке (на сетке, у которой расстояние между точками одинаковое), то для создания матриц, которые будут хранить координаты, поможет функция meshgrid() из библиотеки numpy. Эта функция создает двумерные матрицы сеток по одномерным массивам. Работа этой функции очень наглядно показана в документации к numpy: >>> X, Y = numpy.meshgrid([1,2,3], [4,5,6,7]) >>> X array([[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]) >>> Y array([[4, 4, 4], [5, 5, 5], [6, 6, 6], [7, 7, 7]]) Теперь по индексу узла сетки мы можем узнать реальные координаты: X[0][0] = 1, Y[0][0] = 4 и т.п. Чтобы отделить подготовку данных от самого рисования, создание сетки и расчет функции выделим в отдельную функцию: def makeData (): # Строим сетку в интервале от -10 до 10 с шагом 0.1 по обоим координатам x = numpy.arange (-10, 10, 0.1) y = numpy.arange (-10, 10, 0.1) # Создаем двумерную матрицу-сетку xgrid, ygrid = numpy.meshgrid(x, y) # В узлах рассчитываем значение функции zgrid = numpy.sin (xgrid) * numpy.sin (ygrid) / (xgrid * ygrid) return xgrid, ygrid, zgrid Эта функция возвращает три двумерные матрицы: x, y, z. Координаты x и y лежат в интервале от -10 до 10 с шагом 0.1. Теперь возвращаемся непосредственно к рисованию. Чтобы отобразить наши данные, достаточно вызвать метод plot_surface() экземпляра класса Axes3D, в который передадим полученные с помощью функции makeData() двумерные матрицы. Теперь наш пример выглядит следующим образом: import pylab from mpl_toolkits.mplot3d import Axes3D import numpy def makeData (): x = numpy.arange (-10, 10, 0.1) y = numpy.arange (-10, 10, 0.1) xgrid, ygrid = numpy.meshgrid(x, y) zgrid = numpy.sin (xgrid) * numpy.sin (ygrid) / (xgrid * ygrid) return xgrid, ygrid, zgrid x, y, z = makeData() fig = pylab.figure() axes = Axes3D(fig) axes.plot_surface(x, y, z) pylab.show() Если мы запустим этот скрипт, то появится окно со следующей синей кракозяблой: ![]() Полученную штуку можно вращать с помощью мышки. Matplotlib не использует графический ускоритель, поэтому вращение происходит довольно медленно, хотя скорость зависит от количества точек на поверхности. ![]() ![]() Кроме перечисленных параметров метод plot_surface() имеет дополнительные параметры, которые мы сейчас и рассмотрим. Изменяем внешний вид графикаРассмотрим остальные параметры метода Axes3D.plot_surface(X, Y, Z, *args, **kwargs), их не так много:
Поиграем с каждым из этих параметров. Шаг сеткиСначала посмотрим как влияют на внешний вид параметры rstride и cstride. Изменим в предыдущем примере строку с использованием метода plot_surface() следующим образом: axes.plot_surface(x, y, z, rstride=5, cstride=5) В результате мы получим более мелкую сетку на графике: ![]() Если таким же образом установить значения этих параметров в 20, то сетка будет наоборот более крупная: ![]() Изменение цветаТеперь изменим цвет поверхности с помощью параметра color. Этот параметр представляет собой строку, которая описывает цвет. Строка цвета может задаваться разными способами: Цвет можно определить английским словом для соответствующего цвета или одной буквой. Таких цветов не много:
Для примера сделаем поверхность желтой: axes.plot_surface(x, y, z, color='yellow') ![]() Того же самого результата мы могли бы добиться, используя следующую строку: axes.plot_surface(x, y, z, color='y') Если нам нужен серый цвет, то его яркость мы можем задать с помощью строки, содержащей число в интервале от 0.0 до 1.0 (0 - белый, 1 - черный). Например, мы можем написать следующую строку: axes.plot_surface(x, y, z, color='0.7') В этом случае мы увидим такой вот серый график: ![]() Кроме того мы можем задавать цвет так как это принято в HTML после символа решетки ('#'). Например, можем задать цвет следующим образом: axes.plot_surface(x, y, z, color='#11aa55') Тогда график позеленеет: ![]() Использование цветовых карт (colormap)Цветовые карты используются, если нужно указать в какие цвета должны окрашиваться участки трехмерной поверхности в зависимости от значения Z в этой области (задание цветового градиента). Тема использование градиентов сама по себе большая и интересная, но мы сейчас рассмотрим только некоторые ее аспекты. Чтобы при выводе графика использовался градиент, в качестве значения параметра cmap (от слова colormap, цветовая карта) нужно передать экземпляр класса matplotlib.colors.Colormap или производного от него. Следующий пример использует класс LinearSegmentedColormap, производный от Colormap, чтобы создать градиент перехода от синего цвета к красному через белый. import pylab from mpl_toolkits.mplot3d import Axes3D from matplotlib.colors import LinearSegmentedColormap import numpy def makeData (): x = numpy.arange (-10, 10, 0.1) y = numpy.arange (-10, 10, 0.1) xgrid, ygrid = numpy.meshgrid(x, y) zgrid = numpy.sin (xgrid) * numpy.sin (ygrid) / (xgrid * ygrid) return xgrid, ygrid, zgrid x, y, z = makeData() fig = pylab.figure() axes = Axes3D(fig) axes.plot_surface(x, y, z, rstride=3, cstride=3, cmap = LinearSegmentedColormap.from_list ("red_blue", ['b', 'w', 'r'], 256)) pylab.show() Здесь используется статический метод from_list(), который принимает три параметра:
Если вы не хотите каждый раз создавать свою цветовую карту, то можете воспользоваться одной из уже готовых карт, которые располагаются в модуле matplotlib.cm. Чтобы узнать какие цветовые карты существуют, можно просто прочитать переменную cm._cmapnames, которая представляет собой список строк: from matplotlib import cm print cm._cmapnames В той версии Matplotlib, которая установлена у меня (0.99.1) в консоль вывелись следующие цветовые карты: Spectral, copper, RdYlGn, Set2, summer, spring, Accent, OrRd, RdBu, autumn, Set1, PuBu, Set3, gist_rainbow, pink, binary, winter, jet, BuPu, Dark2, prism, Oranges, gist_yarg, BuGn, hot, PiYG, YlOrBr, Reds, spectral, RdPu, Greens, gist_ncar, PRGn, gist_heat, YlGnBu, RdYlBu, Paired, flag, hsv, BrBG, Purples, cool, Pastel2, gray, Pastel1, gist_stern, GnBu, YlGn, Greys, RdGy, YlOrRd, PuOr, PuRd, gist_gray, Blues, PuBuGn, gist_earth, bone Все их рассматривать смысла нет, просто для примера воспользуемся некоторыми из них: import pylab from mpl_toolkits.mplot3d import Axes3D from matplotlib.colors import LinearSegmentedColormap from matplotlib import cm import numpy def makeData (): x = numpy.arange (-10, 10, 0.1) y = numpy.arange (-10, 10, 0.1) xgrid, ygrid = numpy.meshgrid(x, y) zgrid = numpy.sin (xgrid) * numpy.sin (ygrid) / (xgrid * ygrid) return xgrid, ygrid, zgrid x, y, z = makeData() fig = pylab.figure() axes = Axes3D(fig) axes.plot_surface(x, y, z, rstride=4, cstride=4, cmap = cm.jet) pylab.show() Карта cm.jet - это, наверное, самая часто используемая карта в примерах. ![]() Обратите внимание, что в качестве аргумента cmap мы передаем не строку, а имя переменной из модуля cm, причем это уже созданный экземпляр класса, а не имя класса. Так, например, cm.jet - это экземпляр класса matplotlib.colors.LinearSegmentedColormap. Для примера цветовая карта cm.Spectral выглядит следующим образом: ![]() На этом мы прервемся. Кроме рисование трехмерных поверхностей в Matplotlib существует возможность рисования точек и линий в трехмерном пространстве, трехмерных гистограмм и плоских фигур в трехмерном пространстве. Но другие типы графиков сейчас мы рассматривать не будем, не исключаю, что будут еще другие статьи про Matplotlib. Пожалуйста, оцените материал
|