Построение диаграмм на Python с помощью Matplotlib
25 сентября 2017
Бывает, что нужно по-быстрому визуализировать какие-то данные — построить графики для презентации или вроде того. Есть много способов сделать это. Например, можно открыть CSV-файл в LibreOffice или Google Docs и построить графики в нем. Но что, если диаграммы нужно строить регулярно, а значит предпочтительнее делать это автоматически? Вот тут-то на помощь и приходит Python с его потрясающей библиотекой Matplotlib.
Примечание: Для решения той же задачи в свое время мне доводилось использовать Scala Chart. Однако, как вы сами сейчас убедитесь, Matplotlib куда гибче, да и графики у него получаются намного красивее. Если вас интересует тема визуализации данных, вам стоит обратить внимание на мои стары посты, посвященные Graphviz, а также JavaScript-библиотекам Flot и Dracula.
Не будем ходить вокруг да около, лучше сразу перейдем к примерам. Пожалуй, простейший график, который можно построить в Matplotlib, это график синуса и косинуса:
# vim: set ai et ts=4 sw=4:
import matplotlib as mpl
import matplotlib.pyplot as plt
import math
dpi = 80
fig = plt.figure(dpi = dpi, figsize = (512 / dpi, 384 / dpi) )
mpl.rcParams.update({'font.size': 10})
plt.axis([0, 10, -1.5, 1.5])
plt.title('Sine & Cosine')
plt.xlabel('x')
plt.ylabel('F(x)')
xs = []
sin_vals = []
cos_vals = []
x = 0.0
while x < 10.0:
sin_vals += [ math.sin(x) ]
cos_vals += [ math.cos(x) ]
xs += [x]
x += 0.1
plt.plot(xs, sin_vals, color = 'blue', linestyle = 'solid',
label = 'sin(x)')
plt.plot(xs, cos_vals, color = 'red', linestyle = 'dashed',
label = 'cos(x)')
plt.legend(loc = 'upper right')
fig.savefig('trigan.png')
Я думаю, что код не нуждается в пояснениях. В крайнем случае, вы можете полистать официальную документацию или почитать вывод help()
в REPL’е.
Получившийся график:
Однако на практике часто нужно построить график функции не от абстрактного вещественного числа, а от вполне конкретного времени. И в этом случае хочется, чтобы по оси OX были подписаны не абстрактные 1, 2, 3, а вполне конкретные временные метки. Для примера рассмотрим построение графика регистрации новых доменов в зоне RU за разное время с разбивкой по регистраторам:
# vim: set ai et ts=4 sw=4:
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime as dt
import csv
dates = []
values = {}
with open('ru-newreg.csv', newline = '') as f:
for row in csv.reader(f, delimiter = ',', quotechar = '"'):
if dates == []:
dates = [
dt.datetime.strptime(
"{}-01".format(d),
'%Y-%m-%d'
).date()
for d in row[1:]
]
continue
values[ row[0] ] = row[1:]
dpi = 80
fig = plt.figure(dpi = dpi, figsize = (512 / dpi, 384 / dpi) )
mpl.rcParams.update({'font.size': 10})
plt.title('RU New Domain Names Registration')
plt.xlabel('Year')
plt.ylabel('Domains')
ax = plt.axes()
ax.yaxis.grid(True)
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y'))
ax.xaxis.set_major_locator(mdates.YearLocator())
for reg in values.keys():
plt.plot(dates, values[reg], linestyle = 'solid', label = reg)
plt.legend(loc='upper left', frameon = False)
fig.savefig('domains.png')
Данные были получены с ныне уже закрытого сайта stat.nic.ru, который, впрочем, все еще доступен на web.archive.org. Результирующий график:
Что еще часто строят, это столбчатые диаграммы. В качестве примера возьмем данные из заметки Поиск по географическим данным при помощи PostGIS и построим диаграмму, отображающую сколько точек на карте к какому типу (заправка, кафе и так далее) относятся. Чтобы было чуть интереснее, сделаем вид, что в прошлом году точек каждого вида было на 10% меньше, и попытаемся отразить это изменение:
# vim: set ai et ts=4 sw=4:
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime as dt
import csv
data_names = ['cafe', 'pharmacy', 'fuel', 'bank', 'waste_disposal',
'atm', 'bench', 'parking', 'restaurant',
'place_of_worship']
data_values = [9124, 8652, 7592, 7515, 7041, 6487, 6374, 6277,
5092, 3629]
dpi = 80
fig = plt.figure(dpi = dpi, figsize = (512 / dpi, 384 / dpi) )
mpl.rcParams.update({'font.size': 10})
plt.title('OpenStreetMap Point Types')
ax = plt.axes()
ax.yaxis.grid(True, zorder = 1)
xs = range(len(data_names))
plt.bar([x + 0.05 for x in xs], [ d * 0.9 for d in data_values],
width = 0.2, color = 'red', alpha = 0.7, label = '2016',
zorder = 2)
plt.bar([x + 0.3 for x in xs], data_values,
width = 0.2, color = 'blue', alpha = 0.7, label = '2017',
zorder = 2)
plt.xticks(xs, data_names)
fig.autofmt_xdate(rotation = 25)
plt.legend(loc='upper right')
fig.savefig('bars.png')
Результат:
Те же данные можно отобразить, расположив столбики горизонтально:
# vim: set ai et ts=4 sw=4:
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime as dt
import csv
data_names = ['cafe', 'pharmacy', 'fuel', 'bank', 'w.d.', 'atm',
'bench', 'parking', 'restaurant', 'p.o.w.']
data_values = [9124, 8652, 7592, 7515, 7041, 6487, 6374, 6277,
5092, 3629]
dpi = 80
fig = plt.figure(dpi = dpi, figsize = (512 / dpi, 384 / dpi) )
mpl.rcParams.update({'font.size': 9})
plt.title('OpenStreetMap Point Types')
ax = plt.axes()
ax.xaxis.grid(True, zorder = 1)
xs = range(len(data_names))
plt.barh([x + 0.3 for x in xs], [ d * 0.9 for d in data_values],
height = 0.2, color = 'red', alpha = 0.7, label = '2016',
zorder = 2)
plt.barh([x + 0.05 for x in xs], data_values,
height = 0.2, color = 'blue', alpha = 0.7, label = '2017',
zorder = 2)
plt.yticks(xs, data_names, rotation = 10)
plt.legend(loc='upper right')
fig.savefig('barshoris.png')
Соответствующая диаграмма:
И последний на сегодня вид диаграмм — круговая диаграмма, или «пирог». Для примера попробуем визуализировать распределение кафе по различным городам России:
# vim: set ai et ts=4 sw=4:
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime as dt
import csv
data_names = ['Москва', 'Санкт-Петербург', 'Сочи', 'Архангельск',
'Владимир', 'Краснодар', 'Курск', 'Воронеж',
'Ставрополь', 'Мурманск']
data_values = [1076, 979, 222, 189, 137, 134, 124, 124, 91, 79]
dpi = 80
fig = plt.figure(dpi = dpi, figsize = (512 / dpi, 384 / dpi) )
mpl.rcParams.update({'font.size': 9})
plt.title('Распределение кафе по городам России (%)')
xs = range(len(data_names))
plt.pie(
data_values, autopct='%.1f', radius = 1.1,
explode = [0.15] + [0 for _ in range(len(data_names) - 1)] )
plt.legend(
bbox_to_anchor = (-0.16, 0.45, 0.25, 0.25),
loc = 'lower left', labels = data_names )
fig.savefig('pie.png')
Полученная круговая диаграмма:
Выше была рассмотрена лишь малая часть возможностей Matplotlib. Чтобы получить более полное представление о всей мощи этой библиотеки, советую заглянуть в галерею построенных с ее помощью графиков на официальном сайте. Что же до исходников к этому посту, как обычно, вы найдете их на GitHub.
Дополнение: См также заметки Примеры использования Python-библиотеки NumPy, Визуализация проведенных радиосвязей с помощью Matplotlib и Basemap, Рисуем диаграммы Вольперта-Смита на Python и Моделирование антенн на Python при помощи PyNEC.
Метки: Python.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.