Комиксы xkcd или как сделать няшные графики

автор рубрика
Комиксы xkcd или как сделать няшные графики

Приходилось ли вам делать неформальные презентации? Возможно, вы делали смешные рисунки в одном из графических редакторов (тот же Paint). Но что если их можно сделать при помощи Python в стиле xkcd — популярной платформы с научными веб-комиксами. Для рисования вам понадобится только Python и библиотека matplotlib. В этой статье мы расскажем, как делать визуализацию данных в стиле xkcd, какие шрифты используются и как их настроить.

Данные о вине

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

import pandas as pd

df = pd.read_csv('winemag-data-130k-v2.csv')
df.tail()


           variety    country  price  points                  winery
        Chardonnay     France   45.0      90           William Fèvre
Cabernet Sauvignon  Australia   22.0      90                Tapestry
   Sauvignon Blanc         US   20.0      90                   Fritz
         Primitivo         US   31.0      90                  Hendry
            Sherry      Spain   10.0      90  Bodegas Dios Baco S.L.

Диаграмма рассеяния в xkcd

Полноценные комиксы, как в xkcd, делать с помощью Python делать трудно, хоть и возможно, но диаграммы запросто.

Начнем с самого простого графика — диаграммы рассеяния (scatter plot). Итак, чтобы рисовать в формате xkcd, требуется поместить соответствующий код в контекстный менеджер plt.xkcd(), где plt — это matplotlib.pyplot. И все. Строим графики, так же как делаем всегда (подробнее о визуализации данных на Python с помощью matplotlib тут).

Итак, в matplotlib диаграмма рассеяния строится вызовом функции scatter. Код на Python:

with plt.xkcd():
    plt.figure(figsize=(7, 5))
    plt.scatter(df['price'], df['points'], color = 'hotpink')
    plt.xlabel('Цена')
    plt.ylabel('Баллы')
    plt.title('Соотношение Цена/Баллы')
Диаграмма рассеяния (scatter plot) в xkcd
Диаграмма рассеяния

Возможно шрифты в вашем графике воспроизвелись другие шрифты, но этом далее.

Настраиваем шрифты в xkcd

Библиотека matplotlib будет искать шрифт доступный на компьютере. В комиксах xkcd используется шрифт Humor Sans. Этот шрифт можно найти на просторах Интернета. Если вы его установите и запустите код выше, то вылетит ошибка, потому что Humor Sans не поддерживает кириллицу.

Выйти из этого положения помогает другой шрифт — Comic Sans MS. Это один из шрифтов, который можно использовать в графиках xkcd и который поддерживает кириллицу. Скачайте его и загрузите в директорию со своими шрифтами, например, в ~/.local/share/fonts/. Затем нужно “”пересобрать” шрифты. Для этого перезапустите сеанс Jupyter Notebook (если он используется) и вызовите следующую функцию:

import matplotlib.font_manager as fm

fm._rebuild()

Она настраивает файл ~/.cache/matplotlib/fontlist-v330.json. Можно его удалить вручную, а следующий запуск функций matplotlib сам его создаст. А затем проверьте наличие шрифта:

fm.findfont('Comic Sans MS')
# Результат:
'/home/roman/.local/share/fonts/Comic-Sans-MS.ttf'

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

Просмотреть доступные шрифты matplotlib можно через функцию rcParams.keys:

from matplotlib import rcParams

rcParams.keys()

За шрифт xkcd отвечает поле font.fantasy.

Все виды графиков доступны

Диаграммой рассеяния можно себя не ограничивать. Сначала подготавливаете данные, а затем используете контекстный менеджер и строите подходящие вам графики. Например, мы можем посчитать вина дешевле и дороже $20, а затем построить диаграмму процентного соотношения. Итак, код на Python для нахождения дешевых и дорогих вин:

df['is_cheap'] = df['price'].apply(
    lambda price: 1 if price < 20 else 0
)
n_cheap = df[df['is_cheap'] == 0].is_cheap.count()
n = df.is_cheap.count()
n_pricy = n - n_cheap
price_fraction = [n_cheap, n_pricy]

А код для построения графика круговой диаграммы (pie) выгллядит так:

with plt.xkcd():
    fig, ax = plt.subplots(figsize=(5, 5), dpi=100)
    ax.pie(price_fraction, 
           autopct='%1.1f%%',
           shadow=True,
           startangle=90,
           labels=['дешёвка', 'приемлимо'],
          )
    ax.set_title('Пить или не пить')
Построение круговой диаграммы в Python
Круговая диаграмма

Рисуем что-то несерьезное

Можно не ограничивать себя и, помимо обычных графиков, которые необходимы для анализа данных, рисовать что угодно. Например, изобразим в виде прямой линии модель Кюблер-Росс. Итак код на Python выгладит следующим образом:

labels = ['Отрицание', 'Гнев', 'Торг', 'Депрессия', 'Принятие']
text_add = [
    'У меня ничего\nне получится',
    '*ломает клавиатуру\nоб монитор',
    'Да больно\nэто мне надо',
    'Пригласили на\nинтервью',
    'Получил офер',
]

x = np.arange(len(labels))
y = np.zeros(len(labels))

x_add = []
for x_pos in x:
    x_add.append(x_pos + np.random.uniform(0.2, 0.8))

height_y = -0.2
height_add = 0.2
with plt.xkcd():
    fig, ax = plt.subplots(figsize=(10, 6), constrained_layout=True)
    plt.axis('off')
    ax.set_ylim(-2, 0.7)

    # zorder задает "главенство". Чем больше значение,
    # тем "выше" будет стоять график
    ax.axhline(0, xmin=0.05, c='deeppink', zorder=1)
    ax.scatter(x, y, s=120, c='palevioletred', zorder=2)
    ax.scatter(x, y, s=50, c='darkmagenta',zorder=4)
    ax.scatter(x, y, s=30, c='darkmagenta', zorder=3)

    # Пять стадий
    for x_pos, label in zip(x, labels):
        ax.text(x_pos, height_y, label, ha='center', 
                fontsize=12, color='royalblue')

    # Вертикальные линии
    markerline, stemline, baseline = ax.stem(x_add, 
                                             y + height_add, 
                                             use_line_collection=True)
    plt.setp(baseline, zorder=0)
    plt.setp(markerline, marker=',', color='darkmagenta')
    plt.setp(stemline, color='darkmagenta')

    # Дополнительный текст
    for x_pos, text in zip(x_add, text_add):
        ax.text(x_pos, height_add, text, ha='center',
                fontsize=9, color='blue')


    ax.set_title('Путь программиста', fontweight='bold', 
                 fontfamily='serif', fontsize=16, color='royalblue')
    # 2.18 и 0.6 наиболее приближает текст к центру
    ax.text(2.18,0.6, "Или как Data Scientist'ом", 
        fontfamily='serif', fontsize=12, color='mediumblue', ha='center'
модель Кюблер-Росс
5 стадий принятия

Функция axhline задает главную прямую линию, scatter необходимы для точек. Функция stem создает вертикальные линии, а устанавливают plt.setp. Данный пример показывает, что смешные рисунки могут быть сотворены в Python без помощи инструментов типа Paint. Более того, мы могли бы сделать не прямую линию, а изогнутую с помощью синусоидальной функции (numpy.sin).

 

А ещё больше подробностей о визуализации данных с практическими примерами вы узнаете на специализированном курсе по машинному обучению «VIP: Визуализация данных на языке Python» в лицензированном учебном центре обучения и повышения квалификации разработчиков, менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data в Москве.

Комментировать