Привет, Хабр! На связи Кирилл Мазуров, продуктовый аналитик в 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