5 шагов для создания красивых столбчатых диаграмм с помощью Python

Рассказывать убедительную историю с помощью данных на Python становится намного проще, когда диаграммы, поддерживающие эту самую историю, ясны, не требуют пояснений и визуально приятны для аудитории.

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

t.me/pro_python_code – Python для разработчиков.

5 шагов для создания красивых столбчатых диаграмм с помощью Python

Мотивация

Matplotlib позволяет быстро и легко выводить данные с помощью готовых функций, но этапы настройки требуют больших усилий.
Я потратил довольно много времени на изучение лучших практик построения убедительных диаграмм с помощью Matplotlib, так что вам не придётся этого делать.

В этой статье я сосредоточусь на столбчатых диаграммах и объясню, как я собрал воедино крупицы знаний, которые я нашел. От этого варианта…

5 шагов для создания красивых столбчатых диаграмм с помощью Python

… к этому:

5 шагов для создания красивых столбчатых диаграмм с помощью Python

#0 Данные

Чтобы проиллюстрировать методологию, я использовал общедоступный набор данных о задержках авиакомпаниями внутренних рейсов в США 2008 года:

2008, “Data Expo 2009: Airline on time data”, https://doi.org/10.7910/DVN/HG7NV7, Harvard Dataverse, V1
Public domain CC0 1.0

После импорта необходимых пакетов для считывания данных и построения наших графиков, я просто сгруппировал данные по месяцам и рассчитал среднюю задержку, используя приведённый ниже код:

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
from matplotlib.ticker import MaxNLocator

df = pd.read_csv('DelayedFlights.csv')

df = df[['Month', 'ArrDelay']] # Let's only keep the columns useful to us
df = df[~df['ArrDelay'].isnull()] # Get rid of cancelled and diverted flights

# Group by Month and get the mean
delay_by_month = df.groupby(['Month']).mean()['ArrDelay'].reset_index() 

Набор данных, используемый на протяжении всей статьи для построения различных версий столбчатой диаграммы, выглядит следующим образом:

5 шагов для создания красивых столбчатых диаграмм с помощью Python

#1 Основной график

Справедливости ради, с помощью двух строк кода вы уже можете построить столбчатую диаграмму и получить из неё некоторые основные сведения.
По общему признанию, этот график не самый красивый и не самый полезный, поскольку в нём отсутствует ключевая информация, но вы уже можете сказать, что путешествие в декабре, скорее всего, приведёт к задержке рейса.

# Create the figure and axes objects, specify the size and the dots per inches 
fig, ax = plt.subplots(figsize=(13.33,7.5), dpi = 96)

# Plot bars
bar1 = ax.bar(delay_by_month['Month'], delay_by_month['ArrDelay'], width=0.6)
5 шагов для создания красивых столбчатых диаграмм с помощью Python

#2 Самое необходимое

Давайте добавим несколько важных разделов в нашу таблицу, чтобы сделать её более читаемой для аудитории.

  • Сетки

Для улучшения её удобочитаемости необходимы сетки графика. Их прозрачность установлена на 0,5, чтобы они не слишком мешали точкам данных.

  • Переформатирование по осям X и Y

Я добровольно добавил здесь больше параметров, чем необходимо, чтобы иметь более полное представление о возможностях настройки. Например, оси x не нужны объекты major_formatter и major_locator, поскольку мы только настраиваем метки, но если ось x считывателя состоит из других фигур, то это может пригодиться.

  • Панель меток столбцов

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

# Create the grid 
ax.grid(which="major", axis='x', color='#DAD8D7', alpha=0.5, zorder=1)
ax.grid(which="major", axis='y', color='#DAD8D7', alpha=0.5, zorder=1)

# Reformat x-axis label and tick labels
ax.set_xlabel('', fontsize=12, labelpad=10) # No need for an axis label
ax.xaxis.set_label_position("bottom")
ax.xaxis.set_major_formatter(lambda s, i : f'{s:,.0f}')
ax.xaxis.set_major_locator(MaxNLocator(integer=True))
ax.xaxis.set_tick_params(pad=2, labelbottom=True, bottom=True, labelsize=12, labelrotation=0)
labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
ax.set_xticks(delay_by_month['Month'], labels) # Map integers numbers from the series to labels list

# Reformat y-axis
ax.set_ylabel('Delay (minutes)', fontsize=12, labelpad=10)
ax.yaxis.set_label_position("left")
ax.yaxis.set_major_formatter(lambda s, i : f'{s:,.0f}')
ax.yaxis.set_major_locator(MaxNLocator(integer=True))
ax.yaxis.set_tick_params(pad=2, labeltop=False, labelbottom=True, bottom=False, labelsize=12)

# Add label on top of each bar
ax.bar_label(bar1, labels=[f'{e:,.1f}' for e in delay_by_month['ArrDelay']], padding=3, color='black', fontsize=8) 
5 шагов для создания красивых столбчатых диаграмм с помощью Python

#3 Профессиональный вид

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

  • Шипы

Шипы образуют рамку, видимую вокруг графика. Они удаляются, за исключением правого, который должен быть немного толще.

  • Красная линия и прямоугольник сверху

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

  • Название и подзаголовок

Что такое график без заголовка?

Подзаголовок может быть использован для дальнейшего объяснения содержания.

  • Источник

Должен быть во всех когда-либо созданных графиках.

  • Корректировка отступов

Поля, окружающие область графика, отрегулированы таким образом, чтобы убедиться, что используется всё доступное пространство.

  • Белый фон

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

# Remove the spines
ax.spines[['top','left','bottom']].set_visible(False)

# Make the left spine thicker
ax.spines['right'].set_linewidth(1.1)

# Add in red line and rectangle on top
ax.plot([0.12, .9], [.98, .98], transform=fig.transFigure, clip_on=False, color='#E3120B', linewidth=.6)
ax.add_patch(plt.Rectangle((0.12,.98), 0.04, -0.02, facecolor='#E3120B', transform=fig.transFigure, clip_on=False, linewidth = 0))

# Add in title and subtitle
ax.text(x=0.12, y=.93, s="Average Airlines Delay per Month in 2008", transform=fig.transFigure, ha='left', fontsize=14, weight='bold', alpha=.8)
ax.text(x=0.12, y=.90, s="Difference in minutes between scheduled and actual arrival time averaged over each month", transform=fig.transFigure, ha='left', fontsize=12, alpha=.8)

# Set source text
ax.text(x=0.1, y=0.12, s="Source: Kaggle - Airlines Delay - https://www.kaggle.com/datasets/giovamata/airlinedelaycauses", transform=fig.transFigure, ha='left', fontsize=10, alpha=.7)

# Adjust the margins around the plot area
plt.subplots_adjust(left=None, bottom=0.2, right=None, top=0.85, wspace=None, hspace=None)

# Set a white background
fig.patch.set_facecolor('white')
5 шагов для создания красивых столбчатых диаграмм с помощью Python

#4 Цветовой градиент

График в том виде, в каком мы оставили его в предыдущем разделе, аккуратен и готов к включению в презентацию. Игра с цветом полос и добавление градиента для лучшей визуализации вариаций не являются существенными, но добавят ему привлекательности.

Этот вариант использования не обязательно имеет лучшую документацию в интернете, но на самом деле его не так уж сложно реализовать с помощью функций LinearSegmentedColormap и Normalize Matplotlib.

# Colours - Choose the extreme colours of the colour map
colours = ["#2196f3", "#bbdefb"]

# Colormap - Build the colour maps
cmap = mpl.colors.LinearSegmentedColormap.from_list("colour_map", colours, N=256)
norm = mpl.colors.Normalize(delay_by_month['ArrDelay'].min(), delay_by_month['ArrDelay'].max()) # linearly normalizes data into the [0.0, 1.0] interval

# Plot bars
bar1 = ax.bar(delay_by_month['Month'], delay_by_month['ArrDelay'], color=cmap(norm(delay_by_month['ArrDelay'])), width=0.6, zorder=2)
5 шагов для создания красивых столбчатых диаграмм с помощью Python

#5 Последний штрих

Чтобы получить конечный результат, представленный в начале статьи, единственное, что осталось сделать, это реализовать эти несколько дополнительных компонентов:

  • Средняя линия данных

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

  • Вторая цветовая гамма

Благодаря второй цветовой шкале мы выделяем данные выше среднего (или любого порогового значения), чтобы облегчить восприятие визуализации за короткий промежуток времени.

  • Легенда

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

# Find the average data point and split the series in 2
average = delay_by_month['ArrDelay'].mean()
below_average = delay_by_month[delay_by_month['ArrDelay']<average]
above_average = delay_by_month[delay_by_month['ArrDelay']>=average]
# Colours - Choose the extreme colours of the colour map
colors_high = ["#ff5a5f", "#c81d25"] # Extreme colours of the high scale
colors_low = ["#2196f3","#bbdefb"] # Extreme colours of the low scale

# Colormap - Build the colour maps
cmap_low = mpl.colors.LinearSegmentedColormap.from_list("low_map", colors_low, N=256)
cmap_high = mpl.colors.LinearSegmentedColormap.from_list("high_map", colors_high, N=256)
norm_low = mpl.colors.Normalize(below_average['ArrDelay'].min(), average) # linearly normalizes data into the [0.0, 1.0] interval
norm_high = mpl.colors.Normalize(average, above_average['ArrDelay'].max())

# Plot bars and average (horizontal) line
bar1 = ax.bar(below_average['Month'], below_average['ArrDelay'], color=cmap_low(norm_low(below_average['ArrDelay'])), width=0.6, label='Below Average', zorder=2)
bar2 = ax.bar(above_average['Month'], above_average['ArrDelay'], color=cmap_high(norm_high(above_average['ArrDelay'])), width=0.6, label='Above Average', zorder=2)
plt.axhline(y=average, color = 'grey', linewidth=3)

# Determine the y-limits of the plot
ymin, ymax = ax.get_ylim()
# Calculate a suitable y position for the text label
y_pos = average/ymax + 0.03
# Annotate the average line
ax.text(0.88, y_pos, f'Average = {average:.1f}', ha='right', va='center', transform=ax.transAxes, size=8, zorder=3)

# Add legend
ax.legend(loc="best", ncol=2, bbox_to_anchor=[1, 1.07], borderaxespad=0, frameon=False, fontsize=8)
5 шагов для создания красивых столбчатых диаграмм с помощью Python

#6 Заключительные мысли

Цель этой статьи состояла в том, чтобы поделиться знаниями для построения более убедительной гистограммы с использованием Matplotlib. Я постарался сделать это как можно более практичным с помощью повторно используемых фрагментов кода.

Я уверен, что необходимо внести и другие коррективы, о которых я не подумал. Если у вас есть какие-либо идеи по улучшению, не стесняйтесь комментировать и делать эту статью более полезной для всех!

+1
0
+1
12
+1
0
+1
0
+1
0

Один комментарий

  1. Конечно же красиво, но практичнее было бы что-то подобное реализовать в том же plotly, а не громоздком matplotlib

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *