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

Немного рекламы

Введение

В 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), их не так много:

  • X, Y и Z мы уже использовали - эти параметры задают сетку и значение функции в узлах.
  • rstride и cstride задают шаг вывода графика. Чем меньше шаг, тем точнее отображается функция, но тем дольше происходит рисование.
  • color - задает цвет графика
  • cmap - задает градиент цветов, чтобы цвет ячейки графика зависел от значения функции в этой области.

Поиграем с каждым из этих параметров.

Шаг сетки

Сначала посмотрим как влияют на внешний вид параметры rstride и cstride. Изменим в предыдущем примере строку с использованием метода plot_surface() следующим образом:

axes.plot_surface(x, y, z, rstride=5, cstride=5)

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

Если таким же образом установить значения этих параметров в 20, то сетка будет наоборот более крупная:

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

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

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

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

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

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(), который принимает три параметра:

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

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

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

Рейтинг 4.7/5. Всего 148 голос(а, ов)


Немного рекламы


 11.02.2012 - 22:17

Офигезно... И все без косяков...
Спасибо.

 19.07.2013 - 20:00

Спасибо!
все просто и понятно)))

 31.12.2013 - 20:57

Желтый, синий, серый и зеленый- зачем много раз повторять одно и тоже.

Юрий 16.02.2014 - 14:47

Спасибо огромное!

 01.07.2015 - 12:32

confused smileyrolling eyes smileyshrieking smiley

Sagleft 13.12.2015 - 09:14

За статью спасибо. Но вот долго не мог установить matplotlib и numpy, чтобы в питоне ошибки не выходили. Поставил Anacond'у 3.5 64 bit, всё заработало.

Андрей 16.07.2016 - 13:59

cm._cmapnames

В версии 1.5 cm._cmapnames нет. Вывести список цветовых карт можно с помощью:
cm.datad.keys() или
cm.cmap_d.keys()


Подписаться на комментарии
Автор:
Тема:
 Ваш комментарий
 
 
Введите код 226