{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "# Графическая визуализация данных\n", "\n", " \n", "**С.В. Лемешевский** (email: `sergey.lemeshevsky@gmail.com`), Институт математики НАН Беларуси\n", "\n", "Date: **Mar 31, 2020**\n", "\n", "Одной из важных частей в анализе данных является графическое\n", "визуализация. Это может быть частью исследовательского процесса —\n", "например, чтобы помочь идентифицировать выбросы или необходимые\n", "преобразования данных, или как способ генерирования идей для\n", "моделей. В Python есть много дополнительных библиотек для создания\n", "статических или динамических визуализаций, но мы сосредоточемся в\n", "основном на `matplotlib` и библиотеках, которые построены на её\n", "основе. \n", "\n", "Со временем `matplotlib` породила ряд дополнительных наборов инструментов\n", "для визуализации данных, которые используют `matplotlib` в качестве\n", "«ядра». Одним из таких инструментов является `seaborn`.\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Краткий пример использования `matplotlib`\n", "
\n", "\n", "Для импорта библиотеки `matplotlib` будем использовать следующее\n", "соглашение:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "%matplotlib inline\n", "\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ниже приведен пример построения простой прямой:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "data = np.arange(10)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "data" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "plt.plot(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Рисунки и подграфики\n", "
\n", "\n", "Графики в `matplotlib` находятся внутри объекта `Figure`. Новый\n", "рисунок можно создать с помощью `plt.figure`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "fig = plt.figure()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В интерпретаторе IPython будет построено пустое окно, а в блокноте\n", "Jupyter ничего не произойдет. Нельзя создавать окно с пустым\n", "рисунком. Нужно создать один или несколько подграфиков (subplots),\n", "используя функцию `add_subplot`" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "fig.add_subplot(2, 2, 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Это означает, что рисунок должен быть размером $2 \\times 2$\n", "(т.е. содержать максимум 4 графика), и мы выбрали первый из четырех\n", "графиков (нумерация начинается с единицы). Можно выбрать следующие 2\n", "графика:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ax2 = fig.add_subplot(2, 2, 2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ax3 = fig.add_subplot(2, 2, 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если выполнить команду построения графика, например, `plt.plot([1.5, 3.5, -2, 1.6])`,\n", "вывод будет осуществляться в последний график последнего созданного\n", "рисунка. Например, выполнение команды" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "plt.plot(np.random.randn(50).cumsum(), 'k--')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Выражение `'k--'` задает стиль линии: черная штриховая линия. \n", "Метод `fig.add_subplot` возвращает объект `AxesSubplot`, в который\n", "можно напрямую выводить график:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "_ = ax1.hist(np.random.randn(100), bins=20, color='k', alpha=0.3)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "ax2.scatter(np.arange(30), np.arange(30) + 3 * np.random.randn(30))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Полный каталог типов графиков можно найти на сайте .\n", "\n", "Как вы заметили представленные выше команды, выполняемые в отдельных\n", "ячейках, в блокноте Jupyter не работают. Для того чтобы строить\n", "подграфики в Jupyter нужно все команды выполнять в одной ячейке:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "fig = plt.figure()\n", "fig.add_subplot(2, 2, 1)\n", "ax1 = fig.add_subplot(2, 2, 2)\n", "ax2 = fig.add_subplot(2, 2, 3)\n", "plt.plot([1.5, 3.5, -2, 1.6])\n", "_ = ax1.hist(np.random.randn(100), bins=20, color='k', alpha=0.3)\n", "ax2.scatter(np.arange(30), np.arange(30) + 3 * np.random.randn(30))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Цвет, маркеры и стили линий\n", "
\n", "\n", "Основная функция `plot` библиотеки `matplotlib` принимает массивы\n", "координат `x` и `y` и (опционально) строку, задающую цвет и стиль\n", "линии. Например, для того чтобы построить зависимость `y` от `x`\n", "зелеными штрихами, необходимо выполнить:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```Python\n", " ax.plot(x, y, 'g--')\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Таким образом, мы задали и цвет и стиль линии в виде строки. На\n", "практике при программном создании графиков использование строк не\n", "удобно. Такой же график можно построить с помощью команды:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```Python\n", " ax.plot(x, y, linestyle='--', color='g')\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Графики могут иметь также маркеры для выделения точек данных. Так как\n", "`matplotlib` создает непрерывные линии, интерполируя значения между\n", "заданными точками, может быть не ясно, где находятся заданные\n", "значения. Маркеры могут быть частью строки, задающей стиль линии:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "from numpy.random import randn" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "plt.plot(randn(30).cumsum(), 'ko--')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Это же можно было записать более явно:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```Python\n", " plot(randn(30).cumsum(), color='k', linestyle='dashed', marker='o')\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Как видно, между последовательными точками строится линейная\n", "интерполяция. Это поведение можно изменить с помощью параметра\n", "`drawstyle`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "data = np.random.randn(30).cumsum()\n", "plt.plot(data, 'k--', label='Default')\n", "plt.plot(data, 'k-', drawstyle='steps-post', label='steps-post')\n", "plt.legend(loc='best')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Подписи к осям, масштаб и легенда\n", "
\n", "\n", "Для иллюстрации настройки графиков создадим простой рисунок и\n", "отобразим график случайного блуждания:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "fig = plt.figure()\n", "ax = fig.add_subplot(1, 1, 1)\n", "ax.plot(np.random.randn(1000).cumsum())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Для изменения подписей на оси $x$ воспользуемся методами `set_xticks`\n", "и `set_xticklables`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "fig = plt.figure()\n", "ax = fig.add_subplot(1, 1, 1)\n", "ax.plot(np.random.randn(1000).cumsum())\n", "ticks = ax.set_xticks([0, 250, 500, 750, 1000])\n", "labels = ax.set_xticklabels(['one', 'two', 'three', 'four', 'five'], rotation=30, fontsize='small')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Параметр `rotation` поворачивает метки надписей на оси $x$ на 30\n", "градусов. И, наконец, зададим название графика и метку для оси $x$ с\n", "помощью методов `set_title` и `set_xlabel`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "fig = plt.figure()\n", "ax = fig.add_subplot(1, 1, 1)\n", "ax.plot(np.random.randn(1000).cumsum())\n", "ticks = ax.set_xticks([0, 250, 500, 750, 1000])\n", "labels = ax.set_xticklabels(['one', 'two', 'three', 'four', 'five'], rotation=30, fontsize='small')\n", "ax.set_title('Первый график matplotlib')\n", "ax.set_xlabel('Шаги')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Модификация оси $y$ осуществляется точно также, только нужно заменить\n", "`x` на `y` в приведенном выше коде. У класса осей есть метод `set`,\n", "который допускает пакетную настройку свойств графика. В предыдущем \n", "примере можно было также написать:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```Python\n", " props = {\n", " 'title': 'Первый график matplotlib',\n", " 'xlabel': 'Шаги\n", " }\n", " ax.set(**props)\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Для вывода легенды графика есть несколько способов. Простейший\n", "заключается в передаче аргумента `label` при построении графиков:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "fig = plt.figure(); ax = fig.add_subplot(1, 1, 1)\n", "ax.plot(randn(1000).cumsum(), 'k', label='one')\n", "ax.plot(randn(1000).cumsum(), 'k--', label='two')\n", "ax.plot(randn(1000).cumsum(), 'k.', label='three')\n", "ax.legend(loc='best')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Сохранение рисунков в файл\n", "
\n", "\n", "Можно сохранить активный рисунок в файл с помощью метода\n", "`plt.savefig`. Например, чтобы сохранить рисунок в формате SVG\n", "достаточно набрать:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```Python\n", " plt.savefig('figpath.svg')\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Тип файла определяется расширением. Есть пара важных параметров:\n", "`dpi`, который задает разрешение рисунка (точек на дюйм),\n", "`bbox_inches`, который может обрезать пустое пространство вокруг\n", "рисунка. Например, чтобы сохранить тот же график в формате PNG с\n", "разрешением 400 DPI, нудно выполнить:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```Python\n", " plt.savefig('figpath.png', dpi=400, bbox_inches='tight')\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Функция `savefig` сохраняет не только на диск. Она может записывать\n", "график в любой файлоподобный объект, например в `BytesIO`:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```Python\n", " from io import BytesIO\n", " buffer = BytesIO()\n", " plt.savefig(buffer)\n", " plot_data = buffer.getvalue()\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Таблица 1 : Метод `savefig`: параметры\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
Параметр Описание
fname Строка, содержащая путь к файлу или файлоподобный объект Python. Формат рисунка определяется расширением файла
dpi Разрешение рисунка в точках на дюйм. По умолчанию 100
facecolor, edgecolor Цвет фона рисунка вне графика. По умолчанию w (белый)
format Явное задание формата файла
bbox_inches Часть рисунка для сохранения. Если задано 'tight', 2будет попытка обрезать пустое пространство вокруг
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "# Построение графиков с помощью `pandas` и `seaborn`\n", "
\n", "\n", "Библиотека `matplotlib` может быть инструментом довольно низкого\n", "уровня. График собирается из его базовых компонентов: отображения\n", "данных (т.е. тип графика: линия, полоса, прямоугольник, разброс,\n", "контур и т.д.), легенды, заголовка, меток и других аннотаций. В\n", "библиотеке `pandas` мы можем получить множество столбцов данных, а\n", "также метки строк и столбцов. В `pandas` имеются встроенные методы,\n", "которые упрощают визуализацию объектов `DataFrame` и `Series`. Еще\n", "одна библиотека для статистических графиков — `seaborn`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Линейные графики\n", "
\n", "\n", "Объекты `Series` и `DataFrame` имеют метод `plot` для создания базовых\n", "типов графиков. По умолчанию `plot()` создает линейные графики" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "s = pd.Series(np.random.randn(10).cumsum(), index=np.arange(0, 100, 10))\n", "s.plot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Индекс объекта `Series` передается в `plot` библиотеки `matplotlib`\n", "для оси $x$. При этом такое поведение можно отключить с помощью\n", "параметра `use_index = False`. В таблице\n", "[visual:plt-with-pandas:tbl:1](#visual:plt-with-pandas:tbl:1) дается полный список\n", "параметров функции `Series.plot`.\n", "\n", "Большинство графических методов `pandas` принимают опциональный\n", "параметр `ax`, который может являться объектом `subplot`. Это\n", "позволяет размещать подграфики на сетке.\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Таблица 2 : Параметры метода `Series.plot`
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
Параметр Описани
label Метка для легенды
ax Объект subplot из matplotlib, в который выводится график. Если не задан — вывод идет в активный подграфик
style Строка, задающая стиль графика (например, ko--)
alpha Прозрачность заполнения графика (от 0 до 1)
kind Тип графика. Может быть: 'area' , 'bar' , 'barh' , 'density', 'hist' , 'kde' , 'line' , 'pie'
logy Использовать ли логарифмический масштаб по оси y
use_index Использовать ли объект индекс для меток оси
rot Поворот меток оси
xticks Значения для меток оси x
yticks Значения для меток оси x
xlim Границы по оси x (например, [0, 10])
ylim Границы по оси y
grid Отображать ли сетку по осям (включено по умолчанию)
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Метод `plot` объекта `DataFrame` выводит график для каждого столбца\n", "данных в виде линии на одном и том же подграфике, создавая при этом\n", "легенду автоматически:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "df = pd.DataFrame(np.random.randn(10, 4).cumsum(0), columns=['A', 'B', 'C', 'D'], index=np.arange(0, 100, 10))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "df.plot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Атрибут `plot` содержит «семейство» методов для различных типов\n", "графиков. Например, `df.plot()` эквивалентно `df.plot.line()`.\n", "\n", "В `DataFrame` есть несколько параметры, которые обеспечивают некоторую\n", "гибкость при обработке столбцов. Например, следует ли разместить их\n", "все на одном подграфике или создавать отдельные. В таблице\n", "[visual:plt-with-pandas:tbl:2](#visual:plt-with-pandas:tbl:2) представлены такие параметры.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Таблица 3 : Специфичные для `DataFrame` параметры `plot`
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
Параметр Описание
subplots Рисовать ли каждый столбец DataFrame в отдельном подграфике
sharex Если subplots=True, использовать ли одну и ту же ось x, связывая метки оси
sharey Если subplots=True, использовать ли одну и ту же ось y
figsize Размер рисунка для создания в виде кортежа
title Заголовок рисунка в виде строки
legend Добавлять ли легенду на рисунок (по умолчанию True)
sort_columns Отображать ли столбцы в алфавитном порядке
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Столбчатые диаграммы\n", "
\n", "\n", "Методы `plot.bar()` и `plot.barh()` строят вертикальные и\n", "горизонтальные столбчатые диаграммы. В этом случае индексы объектов\n", "`Series` и `DataFrame` в качестве меток на оси `x` (`bar`) или `y`\n", "(`barh`)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "fig, axes = plt.subplots(2, 1)\n", "data = pd.Series(np.random.rand(16), index=list('abcdefghijklmnop'))\n", "data.plot.bar(ax=axes[0], color='k', alpha=0.7)\n", "data.plot.barh(ax=axes[1], color='k', alpha=0.7)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Параметры `color='k`' и `alpha=0.7` устанавливают цвет графика в\n", "черный и частичную прозрачность для заполнения.\n", "\n", "В `DataFrame` столбчатые диаграммы группируют каждую строку значений\n", "вместе в группу столбиков, соответствующих каждому значению в строке:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "df = pd.DataFrame(np.random.rand(6, 4), index=['one', 'two', 'three', 'four', 'five', 'six'], columns=pd.Index(['A', 'B', 'C', 'D'], name='Genus'))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "df" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "df.plot.bar()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Обратите внимание на то, что имя столбцов `'Genus'` используется в\n", "качестве заголовка легенды. Для создания столбчатых диаграмм с\n", "накоплением для `DataFrame` задается параметр `stacked=True`, в\n", "результате чего значение в каждой строке будут сгруппировано вместе" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "df.plot.barh(stacked=True, alpha=0.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Предположим, что есть набор данных по счетам и чаевым в ресторане, и\n", "нам нужно построить столбчатую диаграмму с накоплением, показывающую\n", "процентное соотношение точек данных для каждого размера группы в\n", "каждый день. Загрузим данные из файла\n", "[tips.csv](src-visual/tips.csv.txt) и создадим сводную по дням и размеру вечеринки\n", "(количество человек):" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "tips = pd.read_csv('src-visual/tips.csv')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "tips.head()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "party_counts = pd.crosstab(tips['day'], tips['size'])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "party_counts" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "party_counts = party_counts.loc[:, 2:5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь нормализуем данные так, чтобы сумма в каждой строке была равна\n", "$1$ и построим столбчатую диаграмму:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "party_pcts = party_counts.div(party_counts.sum(1), axis=0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "party_pcts" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "party_pcts.plot.bar()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Таким образом, видно, что количество участников вечеринок\n", "в данном наборе увеличивается в выходные дни.\n", "\n", "В случае, если требуется агрегировать или суммировать данные перед\n", "построением графика, использование пакета `seaborn` может значительно\n", "упростить задачу. Давайте посмотрим на процент чаевых в день с помощью\n", "библиотеки `seaborn`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "import seaborn as sns" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "tips['tip_pct'] = tips['tip']/(tips['total_bill'] - tips['tip'])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "tips.head()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sns.barplot(x='tip_pct', y='day', data=tips, orient='h')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Функция `barplot` библиотеки `seaborn` принимает параметр `data`, который\n", "может быть объектом `DataFrame`. Остальные параметры ссылаются на\n", "имена столбцов. Поскольку в день имеется несколько наблюдений, то\n", "столбцы диаграммы представляют собой среднее значение параметра\n", "`tip_pct`. Черные линии, нарисованные на столбцах диаграммы,\n", "представляют 95-процентный доверительный интервал (это можно настроить\n", "с помощью опционального параметра). \n", "\n", "Функция `barplot` имеет параметр `hue`, который позволяет разделить\n", "отображение по дополнительному категориальному значению:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sns.barplot(x='tip_pct', y='day', hue='time', data=tips, orient='h')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Гистограммы и графики плотности распределения\n", "
\n", "\n", "*Гистограмма* — это своего рода столбчатая диаграмма, которая дает\n", "дискретное отображение частоты значений. Составим гистограмму\n", "процентных долей от общего счета, используя метод `plot.hist` объекта\n", "`Series`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "tips['tip_pct'].plot.hist(bins=50)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Связанный с гистограммой тип графиков — *график плотности*, \n", "который формируется путем вычисления оценки непрерывного распределения\n", "вероятности, которое могло бы генерироваться наблюдаемыми данными.\n", "Обычная процедура заключается в аппроксимации этого распределение как\n", "смеси «ядер», то есть более простых распределений, таких как нормальное\n", "распределение. Таким образом, графики под графиками плотности также\n", "можно понимать графики оценки плотности ядра\n", "(*K*ernel *D*ensity *E*stimate). Функции `plot.kde` и `plot.density`\n", "строят график плотности, используя подход KDE:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "tips['tip_pct'].plot.kde()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Библиотека `seaborn` упрощает создание гистограмм и графиков плотности\n", "с помощью метода `distplot`, который позволяет одновременно строить как\n", "гистограмму, так и непрерывную оценку плотности. В качестве примера\n", "рассмотрим бимодальное распределение, состоящее из двух разных\n", "стандартных нормальных распределений:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "comp1 = np.random.normal(0, 1, size=200)\n", "comp2 = np.random.normal(10, 2, size=200)\n", "values = pd.Series(np.concatenate([comp1, comp2]))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sns.distplot(values, bins=100, color='k')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Диаграммы рассеяния или точечные графики\n", "
\n", "\n", "Диаграммы рассеяния полезны при изучении связей между двумя одномерными\n", "рядами данных. Например, загрузим набор данных из файла\n", "[macrodata.csv](src-visual/macrodata.csv.txt) проекта\n", "[Statmodels](https://www.statsmodels.org/stable/index.html). Выберем\n", "некоторые переменные и вычислим «логарифмические разности»:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "macro = pd.read_csv('src-visual/macrodata.csv')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "data = macro[['cpi', 'm1', 'tbilrate', 'unemp']]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "trans_data = np.log(data).diff().dropna()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "trans_data[-5:]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь воспользуемся функцией `regplot` библиотеки `seaborn`, которая\n", "строит графики рассеяния и предлагает график линейной регрессии:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sns.regplot('m1', 'unemp', data=trans_data)\n", "plt.title('Зависимость $\\log$ {} от $\\log$ {}'.format('m1', 'unemp'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "При анализе данных полезно иметь возможность просматривать все\n", "диаграммы рассеяния среди группы переменных, т.е. строить, так\n", "называемые, *парные графики* или *матрицу диаграмм рассеяния*. В\n", "библиотеке `seaborn` для этого есть удобная функция `pairplot`,\n", "которая, в частности, поддерживает размещение гистограмм или оценок\n", "плотности каждой переменной по диагонали:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sns.pairplot(trans_data, diag_kind='kde', plot_kws={'alpha': 0.2})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Категориальные данные\n", "
\n", "\n", "Одним из способов визуализации данных с множеством категориальных\n", "переменных является использование сетки фасетов (*facet grid*). В\n", "библиотеке `seaborn` есть удобная функция `catplot`, которая\n", "упрощает создание сетки фасетов:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sns.catplot(x='day', y='tip_pct', hue='time', col='smoker', kind='bar', data=tips[tips.tip_pct < 1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Вместо отображения разными цветами столбцов диаграмм в фасете мы\n", "также можем расширить сетку фасетов, добавив одну строку по времени:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sns.catplot(x='day', y='tip_pct', row='time', col='smoker', kind='bar', data=tips[tips.tip_pct < 1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Функция `catplot` поддерживает другие типы графиков, которые могут быть\n", "полезны. Например, блочные графики, которые показывают медиану,\n", "квартили и выбросы:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "sns.catplot(x='tip_pct', y='day', kind='box', data=tips[tips.tip_pct < 0.5])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Можно создавать свои собственные сетки фасетов,\n", "используя более общий класс `seaborn.FacetGrid` (см.\n", "[документацию seaborn](https://seaborn.pydata.org/)).\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.2" } }, "nbformat": 4, "nbformat_minor": 4 }