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