Красивые графики на Python с нуля. Часть 1
Сегодня Python имеет широкий набор инструментов для аналитики и Data Science, в том числе и для визуализации данных
Для подобных целей Python имеет множество библиотек, но в этой статье мы рассмотрим библиотеку Matplotlib - одно из самых популярных решений для визуализации, и пройдем путь от базовых графиков до качественной визуализации
Установка библиотек
Для этой статьи нам понадобится Matplotlib и Pandas
Чтобы их установить в ваше виртуальное окружение, выполните в консоли следующую команду
pip install matplotlib, pandas
После чего библиотеки установится на ваш компьютер
Версии библиотек в этой статье
Если ваши версии сильно отличаются, то код из статьи может выдавать неожиданные результаты
print(matplotlib.__version__) # '3.7.1' print(pandas.__version__) # 1.5.3
Базовый график
Начнем с основ, создадим фигуру и оси
import matplotlib.pyplot as plt fig, ax = plt.subplots()
Функция subplots()
возвращает кортеж из фигуры и осей
Если вы работаете традиционной среде разработке, такой как PyCharm, а не в интерактивной, например как Jupyter, то следует добавить следующую строчку кода в конце, чтобы график появился
plt.show()
Этим графиком пока никого не удивить, поэтому двигаемся дальше!
Выбираем датасет
Возьмем данные регионального этапа ВОШ по информатике 2023 года, на этом сайте как раз можно удобно скачать результаты в формате csv
Мы будем использовать библиотеку Pandas для работы с таблицей, импортируем её и загрузим датасет с помощью функции read_csv
import pandas as pd df = pd.read_csv('reg_2023.csv') #reg_2023.csv - название таблицы df.head(5)
При выводе нашего датафрейма мы должны получить следующий результат
Предобработка данных
Перед визуализацией нам следует заняться маленькой предобработкой данных. Сохраним только ненулевые значения Sum, чтобы график нес больше информативный характер
Для этого напишем следующую строчку кода
df = df[df.Sum > 0]
В квадратных скобках мы указываем фильтр, по которому чистим данные. В нашем случае - общее число баллов больше нуля
Строим график
Наконец, можем заняться тем, ради чего все собрались! Построим график гистограммы
В Matplotlib есть метод hist
который строит гистограмму, в него нужно передать входные данные, которые могут быть как питоновским списком, так Numpy массивом или Pandas датафреймом (как в нашем случае)
ax.hist(df.Sum)
Стилизуем график
Сейчас будет самое интересное, сделаем график красивым!
Поменяем достаточно большое количество стандартных параметров графика на кастомные и даже не пытайтесь их всех выучить!
Изменим пропорции нашей фигуры и её разрешение
для этого в функцию subplots передадим следующие параметры
Для этого модифицируем строчку кода
fig, ax = plt.subplots(figsize=(10, 6), dpi=150)
Результат наших модификаций
Уберем лишние spines
Из документации Matplotlib: "An axis spine — the line noting the data area boundaries"
Spines, черные полоски по краям графика, ухудшают его внешний вид, поэтому удалим их всех, кроме нижней
Все эти полоски хранятся как атрибут spines у наших осей, получить список их названий можно следующей строчкой кода
[direction for direction in ax.spines] # ['left', 'right', 'bottom', 'top']
Далее, по такому ключу можно обратиться к конкретной spine и менять её свойства
Например, можно сделать их невидимыми
ax.spines['top'].set_visible(False)
Сделаем невидимыми все spine, кроме нижней (т.е. bottom)
Это можно сделать следующим, достаточно читаемым способом
for direction in ax.spines: if direction != 'bottom': ax.spines[direction].set_visible(False)
Или таким, не очень читаемым способом, но зато в одну строчку
[ax.spines[direction].set_visible(False) for direction in ax.spines\ if direction != 'bottom']
Изменим цвет и толщину spines
Черный цвет сильно выделяется на фоне, поэтому сделаем её серой и тонкой
ax.spines['bottom'].set_color('grey') ax.spines['bottom'].set_linewidth(1)
Результат нашей стилизации
Удалим черные черточки (ticks)
Эти черные черточки возле шкалы значений на оси Oy называются ticks. Убрать их можно следующей строчкой кода, изменив их цвет на белый
ax.yaxis.set_tick_params(color='white')
Изменим шрифт
Стандартный шрифт Matplotlib достаточно устарел, поэтому заменим его на что-нибудь современное, например Inter - бесплатный аналог San Francisco Pro Text, шрифта от Apple
Для этого выполним следующую строчку кода
import matplotlib matplotlib.rcParams['font.family'] = 'Inter' # Название шрифта # шрифт должен быть установлен на компьютер
Итог нашей начальной стилизации
Уже не так безнадежно, как казалось в начале! Но не будем останавливаться на достигнутом, теперь поменяем параметры самой гистограммы
Меняем параметры гистограммы
- color - цвет графика
- bins - количество столбцов
- zorder - номер слоя. Чем выше, тем больше объектов будет перекрывать
для этого изменим строчку кода с созданием гистограммы
ax.hist(df.Sum, color='#2F80ED', bins=128, zorder=2)
Результат наших модификаций
Получилось уже достаточно красиво, многие бы на этом и остановились, но не в нашем случае! Продолжим стилизовать график, меняя его параметры
Добавим разнообразия
Для того, чтобы добавить сетку в Matplotlib есть метод grid, в который мы передадим следующие параметры
для этого напишем следующую строчку кода
ax.grid(axis='y', color='#EEEEEE', linestyle='--', zorder=0)
Поскольку zorder нашей сетки будет ниже чем у гистограммы, то она не будет перекрывать наш график
Добавим вертикальные линии и аннотации
Раз график регионального этапа ВОШ, то добавим границы проходных баллов на заключительный этап и подпишем их
В 2023 году проходной балл на заключительный этап ВОШ по информатике составлял 511 баллов для всех классов
Что мы достаточно точно предсказали в 👉 этом посте
Начнем с вертикальных линий
Для них в Matplotlib есть специальный метод axvline, который принимает на вход координату по иксу и уже привычные для нас параметры
Данный метод строит вертикальную линию от края до края графика
ax.axvline(x=511, color='grey', linewidth=2, linestyle='--')
Для аннотации на графике воспользуемся методом annotate и передадим ему следующие параметры
- text - текст аннотации
- xytext - кортеж координат текста
- xy - кортеж координат конца стрелки
- arrowprops - параметры стрелки
Получаем следующую строчку кода
ax.annotate( text="Проходной балл\nна ЗЭ ВОШ (511)", #\n - перенос строки xytext=(250, 450), xy=(505, 350), arrowprops=dict(arrowstyle="->", connectionstyle="arc3, rad=-0.4") )
И вот, что у нас получилось
Графику не хватает только названия, подписей к осям и указания автора, добавим их!
Добавляем текста на график
Остался финальный штрих - подписи к графику, но не все так просто...
В Matplotlib есть встроенный метод для заголовка - title, но он не подойдет в нашем случае
Вызывая fig.text мы будем ставить текст на самой фигуре (а не на оси, как делает title), поэтому с её помощью можно удержать достаточно расстояния от осей
Координаты текста нужно указывать в относительных величинах, будто вся фигура заключена в квадрате от 0 до 1.
Такое позиционирование является плохой практикой, поскольку придется самостоятельно подбирать координаты, от чего этот процесс сложно автоматизировать
fig.text( s='Распределение баллов на региональном этапе ВОШ\nпо информатике', x=0.1, y=1, fontdict={'size': 18, 'weight': 'bold'} ) fig.text( s='Анализ данных для олимпиадников | DS.Talk', x=0.1, y=0.95, fontdict={'size': 12, 'weight': 'light'} )
Параметром fontdict мы передаем словарь с дополнительными аргументами для шрифта, так мы сможем точечно изменить его размеры и вес
Также добавим подпись к оси Ox
ax.set_xlabel('Балл на региональном этапе', color='grey')
Итоговый результат
Наконец! После стольких модификаций и стилизаций, мы закончили график и можем им насладиться
Весь код статьи
import pandas as pd import matplotlib.pyplot as plt import matplotlib df = pd.read_csv('reg_2023.csv') df = df[df.Sum > 0] matplotlib.rcParams['font.family'] = 'inter' # Название шрифта fig, ax = plt.subplots(figsize=(10, 6), dpi=150) for direction in ax.spines: if direction != 'bottom': ax.spines[direction].set_visible(False) ax.spines['bottom'].set_color('grey') ax.spines['bottom'].set_linewidth(1) ax.yaxis.set_tick_params(color='white') ax.hist(df.Sum, color='#2F80ED', bins=64, zorder=2) ax.grid(axis='y', color='#D9D9D9', linestyle='--', zorder=0) ax.axvline(x=511, color='grey', linewidth=2, linestyle='--', zorder=0) ax.annotate( text="Проходной балл\nна ЗЭ ВОШ - 511", xy=(505, 350), xytext=(250, 450), arrowprops=dict(arrowstyle="->", connectionstyle="arc3, rad=-0.4") ) fig.text( s='Распределение баллов на региональном этапе ВОШ\nпо информатике', x=0.1, y=1, fontdict={'size': 18, 'weight': 'bold'} ) fig.text( s='Анализ данных для олимпиадников | DS.Talk', x=0.1, y=0.95, fontdict={'size': 12, 'weight': 'light'} ) ax.set_xlabel('Балл на региональном этапе', color='grey') plt.show()
Алгоритм улучшения качества графика✨
Понравилась статья? Подписывайтесь на канал AD_olimp!
Еще больше аналитики и Data Science в сфере образования по ссылке: https://t.me/AD_olimp