Журнал LinuxFormat - перейти на главную

LXF103:Диаграммы – это просто!

Материал из Linuxformat
Перейти к: навигация, поиск
Matplotlib Графики и диаграммы – несколькими строчками переносимого кода!

Содержание

Строим графопостроитель

Не впечатлены возможностями R по части графиков, которые мы рассматривали в LXF102? Нет проблем – напишите свою собственную программу, которая будет делать все, как надо. Олег Попов готов прийти вам на помощь.

Речь в этой статье пойдет о Matplotlib – мультиплатформенном пакете для создания двумерных диаграмм на языке программирования Python. С его помощью можно отрисовывать самые разнообразные диаграммы, причем объем кода, в большинстве случаев, не превысит несколько десятков строк. Другая замечательная особенность данного пакета – это способность встраивать диаграммы в различные библиотеки графического интерфейса: Tk, wxWidgets, Qt и GTK+.

Итак, для сегодняшнего урока нам потребуются:

  • Язык программирования Python.
  • Библиотека графического интерфейса GTK+.
  • Интерфейс к библиотеке GTK+ для языка PythonPyGTK.
  • Библиотека для работы с многомерными массивами и численными методами NumPy (эта библиотека интенсивно используется Matplotlib для оптимизации обработки числовых данных).
  • Собственно сам пакет Matplotlib.

Я не буду описывать магию установки программ во всех мыслимых вариантах, но скажу, что для большинства дистрибутивов Linux (и для ОС Windows) доступны скомпилированные пакеты, в том числе в виде RPM, Deb и Fink. Если же вы пожелаете воспользоваться исходными текстами, нужно просто распаковать архив в какой-нибудь каталог, а затем выполнить команды:

python setup.py build
sudo python setup.py install

По умолчанию пакет устанавливается в каталог /usr/local/lib/python2.x/site-packages/Matplotlib/.

В этом каталоге следует открыть файл font_manager.py и обратить внимание на строки, указывающие на каталоги со шрифтами:

 X11FontDirectories = [
   # an old standard installation point
   «/usr/X11R6/lib/X11/fonts/TTF/»,
   # here is the new standard location for fonts
   «/usr/share/fonts/»,
   # documented as a good place to install new fonts
   «/usr/local/share/fonts/»,
   # common application, not really useful
   «/usr/lib/openoffice/share/fonts/truetype/»,
   ]

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

Маtplotlib использует конфигурационный файл .Matplotlibrc, который находится в каталоге /usr/local/share/Matplotlib/.Matplotlibrc (для некоторых дистрибутивов Linux – /usr/share/Matplotlib/.Matplotlibrc). Опытным пользователям Linux должно быть очевидно, что можно скопировать этот файл в свой домашний каталог (точнее, в каталог /home/[user]/Matplotlib/.Matplotlibrc) и изменить желаемые настройки. Очень рекомендуется сразу поправить имена шрифтов, используемых по умолчанию, например, на стандартные шрифты TrueType/OpenType от Microsoft или шрифты Postscript, содержащие русские символы. Я установил следующие имена шрифтов:

font.serif              : Verdana
font.sans-serif         : Arial,Verdana
font.cursive            : Arial
font.fantasy            : Comic Sans MS
font.monospace          : Arial

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

 #!/usr/bin/env python
 # -*- coding: UTF-8 -*-

Для строк, содержащих русские символы, также следует указывать кодировку явно:

unistring=u'Это строка в кодировке Unicode - UTF-8'

Рисуем график функции

Чтобы изобразить диаграмму средствами Matplotlib, нужно создать экземпляр класса Figure, который служит контейнером для одного или нескольких экземпляров класса Subplot. Класс Subplot является, в свою очередь, контейнером для одной-единственной диаграммы.

Конструктор класса Figure принимает следующие параметры:

  • figsize = (width,height) – размер изображения в дюймах. Как показали эксперименты, этот параметр не влияет на размер диаграммы в окнах GTK+, он влияет только на размер диаграммы при ее сохранении во внешний файл.
  • dpi – количество точек на дюйм. Этот параметр задает размеры всех элементов диаграмм, включая масштаб шрифтов и линий. Для аккуратного отображения диаграмм на экране дисплея значения этого параметра можно варьировать от 60 до 120. При разрешении монитора 1280х1024 приемлемое качество получится примерно при 75–85 dpi.
  • facecolor – цвет фона для диаграмм.

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

  • edgecolor = цвет рамки.
  • linewidth = 1.0, толщина линий рамки.
  • frameon = True/False – рисовать/не рисовать рамку вокруг диаграмм.

С помощью параметра subplotpars можно задать поля (свободное место) вокруг диаграмм. Для этого существует класс SubplotParam, который создается следующим конструктором:

  params=SubplotParams(left=0.1, bottom=0.1, right=0.9, top=0.9,wspace=0.1,hspace=0.1)

Параметры задаются в десятичных долях от общего размера области рисования. Параметры left, bottom, right и top задают соответственно положение левой, нижней, правой и верхней границ диаграммы. Параметры wspace и hspace задают свободное место между диаграммами по вертикали и горизонтали и нужны только в том случае, если Figure содержит более одной диаграммы.

Пример создания экземпляра Figure:

  fig = Figure(figsize=(4,3), dpi=85,facecolor='white',edgecolor='lightblue',
  linewidth = 4.0, frameon = True,
  subplotpars=SubplotParams(left=0.1, bottom=0.1, right=0.9,
  top=0.9,wspace=0.1,hspace=0.1))

Теперь можно добавить в fig контейнер для диаграммы

  myplot = fig.add_subplot(1,1,1)

Параметры метода add_subplot(numRows,numCols,plotNum) имеют следующий смысл:

  • numRows – количество диаграмм в строке.
  • numCols – количество диаграмм в столбце. Диаграммы при этом нумеруются последовательно, слева направо, построчно, и для каждой диаграммы задается параметр plotNum – номер диаграммы по порядку.

Например:

  plot1=fig.add_subplot(2,2,3)

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

Или:

 plot2=fig.add_subplot(2,2,2)

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


Давайте, к примеру, изобразим график функции y=sin(2*pi*x)*exp(-x) на интервале (0,5) с шагом 0.02 и отрисуем на нем красные окружности (точки) с шагом 0.1. Сначала создадим экземпляр класса-контейнера Figure и добавим в него экземпляр класса subplot:

  #рисуем график функции
  self.figure1 = Figure(figsize=(4,4), dpi=85,facecolor='lavender',
  edgecolor='lavenderblush',linewidth = 12.0,
  frameon = True,subplotpars=SubplotParams(left=0.1, bottom=0.1,
  right=0.9, top=0.9,wspace=0.1,hspace=0.1))
  self.subplot1=self.figure1.add_subplot(1,1,1)#
  self.subplot1.set_title(u' График функции y=sin(2*pi*x)*exp(-x)', fontsize=18)

Matplotlib интенсивно использует модуль NumPy, который содержит библиотеку математических и служебных функций. Одной из них является функция arange(start, stop, step), которая создает массив из последовательности чисел в диапазоне (start, stop) c шагом step. Эта функция часто используется для задания значений осей диаграммы. Определим с ее помощью массивы значений для оси X.

  #массивы значений для оси X
  X1=arange(0.0,5.0,0.1)
  X2=arange(0.0,5.0,0.02)


А вот и сама функция, которую нужно рисовать.

  #определим функцию для графика
  def f(t):
    s1 = sin(2*pi*t)
    e1 = exp(-t)
    return multiply(s1,e1)

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

  #рисуем график функции сплошной синей линией
  line2=self.subplot1.plot(X2,f(X2),color='blue')
  #рисуем график функции для точек X1 маркерами (красными окружностями)
  line1=self.subplot1.plot(X1,f(X1),'ro')

После того, как создан график, экземпляр класса Figure следует упаковать внутрь стандартного виджета gtk.DrawingArea. Для этого нужно создать экземпляр класса FigureCanvas и передать ему в качестве параметра экземпляр Figure:

  #scrolled window
  self.scrolledwindow1 = gtk.ScrolledWindow()
  self.scrolledwindow1.show ()
  self.vbox1.pack_start (self.scrolledwindow1, True,True, 0)
  self.scrolledwindow1.set_border_width ( 8)
  #
  self.canvas = FigureCanvas(self.figure1) # «упаковать» диграмму внутрь gtk.DrawingArea
  self.canvas.set_size_request(700,500) # минимальнй размер области рисования
  self.scrolledwindow1.add_with_viewport(self.canvas)

Поведение диаграммы в Matplotlib достаточно умное: можно увеличивать и уменьшать масштаб, можно экспортировать диаграмму во внешний файл (поддерживаются форматы PNG, JPEG и Postscript). Для более наглядной манипуляции с диаграммами, есть специальный управляющий виджет – NavigationToolbar, который также можно использовать как виджет GTK+.

Столбцовые и круговые диаграммы

Наиболее распространенными видами диаграмм являются столбцовые и круговые, и пакет Matplotlib успешно справляется с их построением. В следующем примере я попытаюсь показать, как строить несколько диаграмм разного вида внутри контейнера Figure.

На сайте Департамента Энергетики США (http://www.eia.doe.gov/ipm/) можно найти разнообразную статистику по мировой добыче нефти и газа. Попробуем оценить долю РФ в мировых запасах нефти и долю, занимаемую РФ в мировой добыче.


Для начала давайте построим столбцовую диаграмму «Мировые запасы нефти на 1 января 2006 года». Опишем исходные данные в виде списка:

  OilReservesbyCountry=(('Saudi Arabia',264.3),('Canada',178.8),('Iran',132.5),('Iraq',115.0),
  'Kuwait',101.5),('UAE',97.8),('Venezuela',79.7),('Russia',60.0), ('Libya',39.1),
  ('Nigeria',35.9), ('United States',21.4),('China',18.3),('Qatar',15.2),
  ('Mexico',12.9),('Algeria',11.4),('Brazil',11.2), ('Kazakhstan',9.0),
  ('Norway',7.7),('Azerbaijan',7.0),
  ('India',5.8),('Rest of World',68.1))

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

  labels=[x[0] for x in OilReservesbyCountry]#Получаем список стран

Совершенно аналогично можно выбрать и второй элемент (запасы нефти):

  dataY1=[x[1] for x in OilReservesbyCountry]

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

  #цвета
  cl_background='#E8D6BB'#фон
  cl_grid='#330000'#линии сетки
  cl_bars=('#330000','#660000','#993300','#996600','#CC6600','#CC6666','#CC9900',
  '#CC9966','#CCCC99','#CCCC00','#C9A24B','#94AB27','#8A8B75','#8A725E','#A080A4',
  '#9C8BBF','#969CE7', '#976CD1','#983BBA','#A92998','#AAAA9A','#EFBEAA','#CCCC66')

Cобственно отрисовку диаграммы вынесем в метод класса drawbarchart:

  def draw_barchart(self,NumChart,Title,DataSource,YLabel):
  '''метод создает столбцовую диаграмму
       параметры :
       numChart- номер диаграммы
       Title- заголовок
       DataSource- исходные данные
      YLabel-метка для оси Y
  '''

Теперь можно создать экземпляр класса subplot:

  self.subplot=self.figure1.add_subplot(2,2,NumChart,axisbg=cl_background)#
  self.figure1.subplots_adjust(left=0.1,bottom=0.05,hspace=0.9)
  self.subplot.set_title(Title,fontsize=14)

Далее нужно подготовить исходные данные для диаграммы и вызывать метод bar класса subplot, который, собственно, и рисует столбцовую диаграмму. Параметр width задает процент заполнения столбцом отведенного ему прямоугольника по ширине. В нашем примере каждый столбец будет занимать 75 % от максимально возможной ширины:

  #массивы значений для оси X
  X=arange(len(DataSource))
  dataY=[x[1] for x in DataSource ]
  width = 0.75
  barchart= self.subplot1.bar(X1, dataY1, width)

Теперь осталось только настроить диграмму:

  self.subplot.set_axisbelow(True)#координатную сетку на задний план
  self.subplot.grid(True)# координатную сетку рисовать
  self.subplot.set_xlim(-1,len(X)+1)# Максимальные и минимальные значения по оси X
  labels=[x[0] for x in DataSource] # Список меток оси х
  self.subplot.set_xticks(X+width/2.,) # Местополжение меток строго по центру столбца
  self.subplot.set_xticklabels(labels,rotation=90) # установить метки для оси X, повернуть их на 90 градусов
  self.subplot.set_ylabel(YLabel) # название оси Y

Для каждого столбца диаграммы мы устанавливаем цвет из списка cl_bars:

  indcolor=0
  for bar in barchart: #
     bar.set_facecolor(cl_bars[indcolor])
     indcolor=indcolor+1

Круговая диаграмма создается тремя строчками кода с вызовом метода pie класса Subplot. Параметры этого метода, на мой взгляд, самоочевидны.

  self.subplot3=self.figure1.add_subplot(2,2,3)#
  self.subplot3.set_title(u'Доля России в мировых запасах',fontsize=14)
  self.subplot3.pie(RussiaPartOilReserves,labels=(u'Россия',u'прочие\n страны '),
  autopct = '%1.1f%%',explode=(0.2,0.2),colors=('#C9A24B','#CCCC99'), shadow=True)

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

Настройка диаграммы

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

Решение этой задачи оказывается на редкость простым. Нужно всего-навсего импортировать из модуля Matplotlib функцию с именем rc:

from matplotlib import rc

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

rc('xtick', labelsize=12,color='white',direction='out') # x tick labels
rc('lines', lw=0.5, color='#FFCF00') # thicker black lines
rc('grid', c='#FFCF00', lw=0.5) # solid gray grid lines
rc('text', color='#FFCF00')
rc('axes', labelcolor='#FFCF00') # axes сolor
rc('xtick', color='#FFCF00') # color of the tick labels
rc('ytick', color='#FFCF00') # color of the tick labels

И напоследок...

Matplotlib имеет множество функций, не описанных в данной статье. На сайте разработчиков можно найти многочисленные примеры практического использования пакета для визуализации самых разнообразных данных. Все, чем мы пользовались в наших примерах – язык программирования Python, GTK+, PyGTK и Matplotlib – распространяется под свободными лицензиями.

И, наконец, в качестве бонуса для самых любознательных отмечу, что все примеры из данной статьи (как и подавляющее количество примеров, распространяемых вместе с библиотекой) без каких-либо модификаций работают в Microsoft Windows XP/2000/2003, а все необходимые для этого ингредиенты можно найти в откомпилированном виде. Переносимость, особенно задаром – это плюс, не так ли? LXF

Полезные ссылки

  • http://www.python.org – официальный сайт Python. Здесь вы можете найти документацию и самую важную информацию об этом замечательном языке программирования.
  • http://www.pygtk.org – интерфейс к GTK+ для языка программирования Python. Особую ценность представляет FAQ по использованию PyGTK.
  • http://numpy.scipy.org/ – библиотека численных методов NumPу. Matplotlib использует эту библиотеку и без нее не работает.
  • http://Matplotlib.sourceforge.net/ – сайт библиотеки Matplotlib: документация, примеры и FAQ.
Персональные инструменты
купить
подписаться
Яндекс.Метрика