Как стать автором
Обновить
Garage Eight
Garage Eight — продуктовая IT-компания

Как создавать понятные и наглядные графики с помощью библиотеки Plotly: 5 советов и примеры кода

Уровень сложностиСредний
Время на прочтение8 мин
Количество просмотров2.4K

Привет, Хабр! На связи Кирилл Мазуров, продуктовый аналитик в Garage Eight. Прошлая статья с советами по визуализации данных собрала больше 70 сохранений, поэтому написал вторую часть и дополнил ее примерами кода. 

В статье делюсь советами на основе личного опыта и классными приемами из гайда по визуализации данных от DataYoga. У ребят есть не только гайд, но и крутейшая книга с материалами о выборе данных и способах их визуализировать. Доступна бесплатно — советую изучить!

Библиотека для визуализации данных Plotly  

В этой статье подробнее рассмотрим одну из самых популярных и мощных библиотек для визуализации данных в Python — Plotly.

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

Существуют и другие хорошо известные инструменты, такие как Matplotlib, Seaborn, ggplot и другие. Однако в своей практике я чаще всего выбираю именно Plotly — за ее гибкость, современный подход и возможность создавать не просто графики, а интерактивные визуализации, которые отлично подходят как для аналитики, так и для презентаций.

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

Библиотеку условно можно разделить на три основные части.

Plotly Express (plotly.express)

Это высокоуровневая обертка для быстрого создания графиков.
Нужно лишь указать DataFrame, выбрать тип графика и задать оси — всё остальное Plotly делает за вас. Идеально подходит для быстрой визуализации и EDA (исследовательского анализа данных).

Plotly Graph Objects (plotly.graph_objects)

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

Plotly Dash (dash)

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

В примерах этой статьи мы сосредоточимся на Plotly Express и Graph Objects, чтобы показать, как быстро построить график и как при желании его глубоко настроить. 

Совет 1. Сфокусируйтесь на главном

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

Последовательное изложение от ключевого тезиса к аргументам поможет полностью раскрыть тему и донести информацию до аудитории
Последовательное изложение от ключевого тезиса к аргументам поможет полностью раскрыть тему и донести информацию до аудитории

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

В левом примере шкала и линия тренда поверх столбцов только путает зрителя — в правом же конкретные значения дают контекст о том, насколько сильны различия в данных
В левом примере шкала и линия тренда поверх столбцов только путает зрителя — в правом же конкретные значения дают контекст о том, насколько сильны различия в данных

Как бы этот график мог выглядеть с помощью Plotly?

import plotly.graph_objects as go

x = ['A', 'B', 'C', 'D', 'E']
y = [5, 8, 6.3, 3.2, 4.6]


fig = go.Figure(data=[
   go.Bar(
       x=x,
       y=y,
       text=y,
       textposition='outside',
       marker_color='rgba(120, 130, 140, 0.9)',
       width=0.5
   )
])


fig.update_layout(
   plot_bgcolor='rgba(0,0,0,0)',
   paper_bgcolor='rgba(0,0,0,0)',
   xaxis=dict(
       showline=True,
       linecolor='gray',
       showgrid=False,
       zeroline=False
   ),
   yaxis=dict(
       showgrid=False,
       zeroline=False,
       visible=False
   ),
   font=dict(
       size=16,
       color='gray'
   ),
   margin=dict(l=20, r=20, t=20, b=40)
)

fig.show()

Совет 2. Разделяйте информацию

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

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

Как это исправить: 

  • Если информации много и нет единого акцента, лучше разделить диаграммы и уделить внимание каждой. 

  • Если же есть более значимый тренд, можно выделить его цветом или добавить интерактивность. 

Совет 3. Уточняйте детали

Перегружать диаграммы визуальным шумом не стоит, а обозначить важные данные можно. Например, если нужно добавить подпись с объяснением к конкретному пику или провалу, сделайте это рядом с показателем. Так зрителю не придется изучать сначала график, потом подпись, а дальше пытаться понять, к какой из частей диаграммы относится комментарий. 

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

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

Рейтинг в таблицах можно отразить насыщенностью цвета, количество — горизонтальными блоками, а позитивный или негативный тон — привычными оттенками зеленого и красного
Рейтинг в таблицах можно отразить насыщенностью цвета, количество — горизонтальными блоками, а позитивный или негативный тон — привычными оттенками зеленого и красного

Цвет поможет отразить насыщенность, концентрацию или корреляцию и расставить акценты в нужных местах.

Чаще всего самые темные участники графика — переменные с наибольшей корреляцией
Чаще всего самые темные участники графика — переменные с наибольшей корреляцией

 Пример кода для создания подобных визуализаций: 

import plotly.express as px
import pandas as pd
import numpy as np

np.random.seed(42)
variants = ['Вариант А', 'Вариант Б', 'Вариант В', 'Вариант Г', 'Вариант Д']
diff_df = pd.DataFrame({
   'country': variants,
   'relative_diff_users': np.random.uniform(-80, 80, size=5),
   'relative_diff_vol': np.random.uniform(-80, 80, size=5),
   'relative_diff_deps': np.random.uniform(-80, 80, size=5)
})


pivot_pct_change_sorted = diff_df[['country', 'relative_diff_users', 'relative_diff_vol', 'relative_diff_deps']]
pivot_pct_change_sorted.set_index('country', inplace=True)
pivot_pct_change_sorted.columns = ['Metric 1', 'Metric 2', 'Metric 3']


fig = px.imshow(
   pivot_pct_change_sorted,
   labels={'x': 'Metric', 'y': 'Вариант', 'color': 'Изменение, %'},
   title='Изменения по метрикам в сравнении с предыдущим периодом',
   color_continuous_scale='RdYlGn',
   color_continuous_midpoint=0,
   text_auto=False,
   zmin=-100,
   zmax=100,
   aspect='auto'
)


text_values = pivot_pct_change_sorted.applymap(lambda x: f"{x:.1f}%")


fig.update_traces(
   text=text_values.values,
   texttemplate="%{text}",
   hovertemplate=(
       "Вариант: %{y}<br>"
       "Метрика: %{x}<br>"
       "Изменение: %{z:.1f}%<extra></extra>"
   )
)


fig.update_layout(
   autosize=True,
   margin=dict(l=20, r=20, t=50, b=20),
   coloraxis_colorbar=dict(
       title="Изменение",
       ticksuffix="%"
   ),
   font=dict(size=12),
   xaxis=dict(tickangle=-45),
   height=max(600, len(pivot_pct_change_sorted) * 30 + 100)
)


config = {
   'responsive': True,
   'displayModeBar': True,
   'scrollZoom': True
}


fig.show(config=config)

Совет 4. Добавьте контекст

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

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

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

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

import plotly.graph_objects as go
import plotly.io as pio
import pandas as pd
import numpy as np

pio.templates.default = "presentation"

np.random.seed(42)
date_range = pd.date_range(start="2024-08-01", periods=12, freq='W-SUN')


x = pd.DataFrame({
   'week_start': date_range.tolist() * 2,
   'type': ['test'] * 12 + ['control'] * 12,
   'metric_value': np.random.normal(loc=100, scale=10, size=24).round(2)
})


column = 'metric_value'
textposition_1 = 'top center'
textposition_2 = 'bottom center'
title = 'Test vs Control Over Time'
xaxis_title = 'Week Start'
yaxis_title = 'Metric Value'


fig = go.Figure()


fig.add_trace(go.Scatter(
   x=x['week_start'].loc[x['type'] == 'test'],
   y=round(x[column].loc[x['type'] == 'test']),
   mode='lines+markers+text',
   line_shape='linear',
   name='test group',
   text=round(x[column].loc[x['type'] == 'test']),
   textposition=textposition_1,
   texttemplate='%{text}',
   hoverinfo='text+name',
   line=dict(color='darkseagreen'),
   marker=dict(color='darkseagreen'),
   textfont=dict(
       size=16
   )
))


fig.add_trace(go.Scatter(
   x=x['week_start'].loc[x['type'] == 'control'],
   y=round(x[column].loc[x['type'] == 'control']),
   mode='lines+markers+text',
   line_shape='linear',
   name='control group',
   text=round(x[column].loc[x['type'] == 'control']),
   textposition=textposition_2,
   texttemplate='%{text}',
   hoverinfo='text+name',
   line=dict(color='firebrick'),
   marker=dict(color='firebrick'),
   textfont=dict(
       size=14
   )
))


fig.add_vrect(x0="2024-09-22", x1="2024-10-20",
             annotation_text="период теста", annotation_position="top left",
             annotation=dict(font_size=18, font_family="Times New Roman"),
             fillcolor="lightslategray", opacity=0.25, line_width=0)


fig.update_layout(title=title,
                 xaxis_title=xaxis_title,
                 yaxis_title=yaxis_title)


fig.show()

Совет 5. Не забывайте о легенде

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

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

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

import plotly.io as pio
pio.templates.default = "ggplot2"
# pio.templates посмотреть все доступные темы


   # ['ggplot2', 'seaborn', 'simple_white', 'plotly',
   #      'plotly_white', 'plotly_dark', 'presentation', 'xgridoff',
   #      'ygridoff', 'gridon', 'none']

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

import plotly.express as px
import plotly.graph_objects as go
import plotly.io as pio
import pandas as pd
import numpy as np

pio.templates.default = "ggplot2"

np.random.seed(0)
data = pd.DataFrame({
   'metric_1': np.random.randint(10000, 10000000, size=5),
   'option': ['A', 'B', 'C', 'D', 'E'],
   'metric_2': np.random.randint(10, 100, size=5)
})


fig = px.bar(
   data.sort_values(by='metric_1', ascending=False),
   x='metric_1',
   y='option',
   text=[f"${cf:,.0f}" for cf in data['metric_1']],
   color_discrete_sequence=['#96d1fa'],
   log_x=True,
   orientation='h'
)


fig.update_layout(
   title=f"Total metric 1st quarter: ${data['metric_1'].sum():,.0f}",
   xaxis_title="Metric 1 (log)",
   yaxis_title="Option"
)


fig.update_traces(textposition="outside", showlegend=False)


fig.add_trace(
   go.Scatter(
       x=data['metric_2'],
       y=data['option'],
       mode='markers+text',
       text=[f"{u:,.0f}" for u in data['metric_2']],
       textposition='middle right',
       marker=dict(
           color='red',
           symbol='circle',
           size=data['metric_2'],
           sizemode='area',
           sizeref=max(data['metric_2']) / 60,
       ),
       name='Metric 2',
       showlegend=False
   )
)

fig.update_xaxes(tickformat='$,.0f', title_text="Metric 1")
fig.update_yaxes(title_text="Option")

fig.show()

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


Если пропустили первую часть, читайте и сохраняйте!

Больше советов о том, как визуализировать данные, ищите у DataYoga и подписывайтесь на канал команды Garage Eight в Телеграме. Там пишем о дизайне, аналитике, разработке и не только <3

Теги:
Хабы:
+11
Комментарии0

Публикации

Информация

Сайт
garage-eight.com
Дата регистрации
Дата основания
Численность
201–500 человек
Местоположение
Россия