Основы программирования в Python

Алла Тамбовцева

Основы работы с библиотекой matplotlib

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

Для начала построим простенький график для визуализации данных в двух списках. Импортируем модуль pyplot из библиотеки и добавим питоновскую "магическую" строчку для того, чтобы графики отображались прямо в ipynb-файле.

In [5]:
import matplotlib.pyplot as plt
% matplotlib inline

Создадим два небольших списка.

In [6]:
X = [-2, -0.5, 0, 2, 5, 8, 9, 10]
Y = [4, 0.25, 0, 4, 25, 64, 81, 100]

Построим график.

In [7]:
plt.plot(X,Y)
Out[7]:
[<matplotlib.lines.Line2D at 0x7fca728b2a20>]

Как можно заметить, в списке Y сохранены элементы списка X, возведенные в квадрат. Однако наш график не похож на ветвь параболы, он какой-то угловатый. Это нормально, потому что в списках у нас всего по 8 элементов, то есть, всего 8 точек на графике соединяются линиями. Если бы точек было больше, график был бы более гладким. Воспользуемся функцией linspace из библиотеки numpy (вот она нам и пригодилась!).

In [8]:
import numpy as np
In [9]:
x = np.linspace(-2, 10, 100) # 100 точек
x
Out[9]:
array([-2.        , -1.87878788, -1.75757576, -1.63636364, -1.51515152,
       -1.39393939, -1.27272727, -1.15151515, -1.03030303, -0.90909091,
       -0.78787879, -0.66666667, -0.54545455, -0.42424242, -0.3030303 ,
       -0.18181818, -0.06060606,  0.06060606,  0.18181818,  0.3030303 ,
        0.42424242,  0.54545455,  0.66666667,  0.78787879,  0.90909091,
        1.03030303,  1.15151515,  1.27272727,  1.39393939,  1.51515152,
        1.63636364,  1.75757576,  1.87878788,  2.        ,  2.12121212,
        2.24242424,  2.36363636,  2.48484848,  2.60606061,  2.72727273,
        2.84848485,  2.96969697,  3.09090909,  3.21212121,  3.33333333,
        3.45454545,  3.57575758,  3.6969697 ,  3.81818182,  3.93939394,
        4.06060606,  4.18181818,  4.3030303 ,  4.42424242,  4.54545455,
        4.66666667,  4.78787879,  4.90909091,  5.03030303,  5.15151515,
        5.27272727,  5.39393939,  5.51515152,  5.63636364,  5.75757576,
        5.87878788,  6.        ,  6.12121212,  6.24242424,  6.36363636,
        6.48484848,  6.60606061,  6.72727273,  6.84848485,  6.96969697,
        7.09090909,  7.21212121,  7.33333333,  7.45454545,  7.57575758,
        7.6969697 ,  7.81818182,  7.93939394,  8.06060606,  8.18181818,
        8.3030303 ,  8.42424242,  8.54545455,  8.66666667,  8.78787879,
        8.90909091,  9.03030303,  9.15151515,  9.27272727,  9.39393939,
        9.51515152,  9.63636364,  9.75757576,  9.87878788, 10.        ])
In [10]:
plt.plot(x, x**2)
Out[10]:
[<matplotlib.lines.Line2D at 0x7fca728561d0>]

Так график больше похож на параболу. Могли бы изобразить ее полностью, определенную на участке от -10 до 10.

In [11]:
x = np.linspace(-10, 10, 200)
plt.plot(x, x**2)
Out[11]:
[<matplotlib.lines.Line2D at 0x7fca727c6780>]

Поменяем цвет линии по умолчанию на какой-нибудь другой.

In [12]:
plt.plot(x, x**2, 'lightblue')
Out[12]:
[<matplotlib.lines.Line2D at 0x7fca72735978>]

Или так:

In [14]:
plt.plot(x, x**2, 'red')
Out[14]:
[<matplotlib.lines.Line2D at 0x7fca7268bac8>]

Список цветов в Python см. здесь.

Теперь изменим тип линии. По умолчанию используется сплошная линия, но ее можно заменить на пунктирную или что-то подобное:

In [17]:
plt.plot(x, x**2, 'red', linestyle = '--')
Out[17]:
[<matplotlib.lines.Line2D at 0x7fca725fb940>]
In [18]:
plt.plot(x, x**2, 'red', linestyle = '-.')
Out[18]:
[<matplotlib.lines.Line2D at 0x7fca72564e10>]

Список всех типов линий см. здесь.

Кроме того, можно изменить толщину линии, добавив аргумент linewidth.

In [24]:
plt.plot(x, x**2, 'red', linewidth = 3)
Out[24]:
[<matplotlib.lines.Line2D at 0x7fca721eaa58>]

Теперь построим график, состоящий только из точек (можно считать диаграммой рассеяния).

In [25]:
plt.scatter(X, Y)
Out[25]:
<matplotlib.collections.PathCollection at 0x7fca721d69e8>

Теперь будем менять цвет точек и тип точек (тип маркера) одновременно.

In [26]:
plt.scatter(X, Y, color ='red', marker = 'o')
Out[26]:
<matplotlib.collections.PathCollection at 0x7fca7213fa20>
In [27]:
plt.scatter(X, Y, color ='red', marker = '*')
Out[27]:
<matplotlib.collections.PathCollection at 0x7fca720a5a20>
In [28]:
plt.scatter(X, Y, color ='green', marker = '2')
Out[28]:
<matplotlib.collections.PathCollection at 0x7fca7208b9b0>

Список маркеров смотри здесь.

Если бы все строки кода со scatter() были в одной ячейке, то графики бы просто накладывались друг на друга.

In [30]:
plt.scatter(X, Y, color ='red', marker = 'o')
plt.scatter(X, Y, color ='blue', marker = '*')
plt.scatter(X, Y, color ='green', marker = '2')
Out[30]:
<matplotlib.collections.PathCollection at 0x7fca71f63d30>

Если присмотреться, то на красных точках можно увидеть синие звездочки и зеленые треугольники. Чтобы такого не происходило (например, если вы создаете и сохраняете графики в цикле в пределах одной ячейки), нужно добавить строку с функцией clf(), которая очищает координатную плоскость для следующего графика (clf ‒ от clear figure).

In [31]:
plt.scatter(X, Y, color ='red', marker = 'o')
plt.clf()
plt.scatter(X, Y, color ='blue', marker = '*')
Out[31]:
<matplotlib.collections.PathCollection at 0x7fca7236b470>

На плоскости представлен только последний график с синими звездочками, красные точки от первого графика был стерты с помощью clf().

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

In [32]:
x = np.linspace(-100, 100, 100)
y = 1/x
plt.plot(x, y)
Out[32]:
[<matplotlib.lines.Line2D at 0x7fca71f32898>]

Полученный график не совсем похож на гиперболу! Как известно, в точке $x=0$ график уходит на бесконечность, линия при $x=0$ отсутствует. А здесь она есть! Избавимся от нее, построив график "по кусочкам".

In [35]:
x1 = np.linspace(-100, 0, 100) # x < 0
y1 = 1/x1

x2 = np.linspace(0, 100, 100) # x > 0
y2 = 1/x2

plt.plot(x1, y1, 'blue')
plt.plot(x2, y2, 'blue')
/home/oem/.local/lib/python3.5/site-packages/ipykernel_launcher.py:2: RuntimeWarning: divide by zero encountered in true_divide
  
/home/oem/.local/lib/python3.5/site-packages/ipykernel_launcher.py:5: RuntimeWarning: divide by zero encountered in true_divide
  """
Out[35]:
[<matplotlib.lines.Line2D at 0x7fca71ecdcf8>]

В завершение попробуем построить целый рисунок (figure), состоящий сразу из нескольких графиков (подграфиков). Построим разные типы функций: $y=x^2$, $y=x^3$, $y=e^x$ и $y=|x|$. Сначала создадим соответствующие массивы значений:

In [36]:
x = np.linspace(-100, 100, 100)
y = x ** 2
z = x ** 3
r = np.exp(x)
m = abs(x)

Создадим рисунок (figure):

In [37]:
plt.figure(1)
Out[37]:
<matplotlib.figure.Figure at 0x7fca71ed5240>
<matplotlib.figure.Figure at 0x7fca71ed5240>

А теперь будем добавлять в него графики, указывая их расположение. В функции subplot() указывается число. Первые две цифры ‒ это число графиков в строке и столбце (здесь 2 на 2, поэтому 22). Последняя цифра ‒ это положение графика: левый верхний угол (1), правый верхний угол (2), левый нижний угол (3), правый нижний угол (4).

In [39]:
plt.subplot(221)
plt.plot(x, y) # x^2
plt.title('parabola')
plt.grid(True)

plt.subplot(222)
plt.plot(x, z) # x^3
plt.title('hyperbola')
plt.grid(True)

plt.subplot(223)
plt.plot(x, r) # e^x
plt.title('exponent')
plt.grid(True)

plt.subplot(224)
plt.plot(x, m) # |x|
plt.title('abs')
plt.grid(True)

Строка plt.grid(True) нужна для того, чтобы на графиках были добавлены линии разметки, привычные нам "клеточки", которые позволяют удобным образом определять координаты точек на графике.

И напоследок: как сохранить график в файл. Очень просто. Например, так.

In [40]:
plt.figure(1)
plt.scatter(X, Y, color ='red', marker = '*')
plt.savefig('MyScatter.png') # ищем файл в рабочей папке (рядом с текущим ipynb-файлом)

Мы достаточно кратко обсудили возможности библиотеки matplotlib, но на этом ее возможности не заканчиваются. Кому интересно, стоит посмотреть документацию по matplotlib, а также заглянуть в галерею с примерами графиков, которые можно адаптировать под свои задачи и данные.

Если кто-то привык работать в R и полюбил вид графиков ggplot2, можно установить библиотеку ggplot для Python. Кроме того, можно поработать с библиотекой seaborn, она предоставляет много возможностей для построения статистических графиков, и выглядят ти графики тоже очень симпатично.

Если хочется более продвинутой интерактивной графики, связанной со статистическими моделями, стоит обратить внимание на библиотеку GraphLab. Она интересна не только графикой, но и другими вещами, но есть один минус: библиотека платная. Однако есть возможность получить доступ по учебной лицензии и даже продлевать ее, особенно, если учесть, что GraphLab используется в некоторых курсах на Coursera, что тоже облегачает получение доступа (пример такого курса).