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

от автора

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


ссылка на оригинал статьи https://habr.com/ru/articles/908950/


Комментарии

Добавить комментарий

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