April 23

Гайд по библиотеке Plotly

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

Введение в Plotly

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

  • Интерактивность: графики можно вращать, выделять части, применять зум, фильтрацию и др.
  • Широкий набор типов графиков: от классических линейных до 3D-сеток, тепловых карт, диаграмм разреза и карт.
  • Легкость интеграции с Dash (фреймворк для создания веб-приложений), Jupyter Notebook, а также другими средствами BI.
  • Гибкость настроек: можно тонко настраивать каждую деталь — цвета, подписи, легенду, оси, фон и т.д.

Установка

!pip install plotly

Примечание: Библиотека Plotly часто уже встроена в различные среды (например, Google Colab). Убедитесь, что у вас установлена актуальная версия.

Ключевой функционал Plotly

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

Plotly Express (px) и Graph Objects (go)

  • px — упрощённый API для «быстрого старта»: несколько строк — готов интерактивный график (px.bar, px.line, px.scatter_3d, px.choropleth и т. д.).
  • go — низкоуровневый API для тонкой настройки: создание go.Figure(), добавление одного или нескольких go.Traces (go.Scatter, go.Bar, go.Surface) и их исправление через fig.update_traces(), fig.update_layout().

Объект Figure и traces

  • fig = go.Figure() или результат px.* — это контейнер для одного или нескольких «trace» (один trace = одна серия данных, например линия или столбцы).
  • fig.add_trace(...) или у px внутри создаются trace автоматически.

Настройка оформления через update_layout / update_traces

  • fig.update_layout(...) — глобальные параметры: заголовок, подписи осей (xaxis_title, yaxis_title), отступы (margin), шаблон (template="plotly_white"), легенду, сетку и т. д.
  • fig.update_traces(...) — меняет свойства конкретных trace’ов: маркеры, линии, подписи точек (texttemplate, hovertemplate), порядок слоев и т. д.

Шаблоны и цветовые палитры

  • Встроенные шаблоны (template): "plotly", "plotly_white", "ggplot2", "seaborn", "simple_white", "presentation".
  • color_discrete_sequence для категорий и color_continuous_scale для градиентов. Можно использовать списки из px.colors.sequential, px.colors.diverging или определять свою палитру.

Интерактивность и рендер

  • Hover: инструментальные подсказки выводят подробную информацию при наведении. Кастомизируются через hovertemplate.
  • Zoom / Pan: встроенные кнопки позволяют увеличить фрагмент графика и вернуться назад.

Анимация и интерактивные элементы

  • animation_frame / animation_group в px автоматически создают слайдер и кнопки Play/Pause.
  • Для более сложной навигации используются updatemenus (кнопки) и sliders в fig.layout.

Начальный уровень

На этом уровне мы рассмотрим основные типы диаграмм: линейный график, столбчатая диаграмма, гистограмма, круговая диаграмма и т. д. Эти визуализации подходят для первого знакомства и для решения базовых аналитических задач.

Линейный график (Line Chart)

Описание задачи
Построить временной ряд продаж по месяцам или любым другим периодам. Типовая задача бизнес-аналитики — понять динамику показателя во времени.

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

Пример кода

import plotly.graph_objs as go

# Данные для примера
months = ["Янв", "Фев", "Мар", "Апр", "Май", "Июн"]
sales = [1500, 1800, 2100, 1600, 2400, 2200]

# Создаем объект Figure
fig = go.Figure()

# Добавляем линейный график
fig.add_trace(
    go.Scatter(
        x=months,
        y=sales,
        mode='lines+markers',
        name='Продажи',
        line=dict(width=2),       # толщина линии
        marker=dict(size=6)       # размер маркеров
    )
)

# Оформление
fig.update_layout(
    title="Динамика продаж по месяцам",
    xaxis_title="Месяц",
    yaxis_title="Объем продаж",
    template="plotly_white",     # светлая тема оформления
    font=dict(size=14)
)

fig.show()
Линейный график

Столбчатая диаграмма (Bar Chart)

Описание задачи
Сравнить величины (например, количество товаров, проданных в разных регионах) для нескольких категорий.

Тип графика
Столбчатые диаграммы идеально подходят для сравнения категорий (размеры, суммы, количества и т. д.).

Пример кода

import plotly.graph_objs as go

regions = ["Север", "Юг", "Восток", "Запад"]
values = [150, 200, 100, 180]

fig = go.Figure()
fig.add_trace(
    go.Bar(
        x=regions,
        y=values,
        name='Продажи по регионам'
    )
)

fig.update_layout(
    title="Сравнение продаж по регионам",
    xaxis_title="Регион",
    yaxis_title="Продажи",
    template="plotly_white",
    font=dict(size=14)
)

fig.show()
Столбчатая диаграмма

Гистограмма (Histogram)

Описание задачи
Посмотреть распределение непрерывной переменной (например, распределение цен товаров или возрастов клиентов).

Тип графика
Гистограммы подходят для отображения распределения данных по интервалам (бинам).

Пример кода

import plotly.express as px
import numpy as np

# Генерация случайных данных (например, цены)
prices = np.random.normal(loc=500, scale=100, size=150)

fig = px.histogram(
    x=prices,
    nbins=20,  # Количество столбцов (бинов) в гистограмме
    labels={'x': 'Цена'},
    title='Распределение цен'
)
fig.update_layout(yaxis_title='Количество', template="plotly_white", font=dict(size=14))
fig.show()
Столбчатая диаграмма

Круговая диаграмма (Pie Chart)

Описание задачи
Показать долю категорий от общего объема (например, доли рынка конкурентов, процент затрат на различные статьи расходов и т. д.).

Тип графика
Круговая диаграмма наглядно демонстрирует долевые соотношения.

Пример кода

import plotly.express as px

categories = ["Аренда", "Зарплата", "Маркетинг", "Сырье", "Прочее"]
costs = [5000, 8000, 2000, 4000, 1000]

fig = px.pie(
    names=categories,
    values=costs,
    title="Структура расходов"
)
fig.update_traces(
    textposition='inside',
    textinfo='percent+label'  # Отображаем и проценты, и подпись
)
fig.update_layout(template="plotly_white", font=dict(size=14))
fig.show()
Круговая диаграмма

Диаграмма рассеяния (Scatter plot)

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

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

Пример кода

import plotly.express as px
import numpy as np

np.random.seed(42)
ad_spent = np.random.randint(100, 1000, 50)  # Затраты на рекламу
sales = ad_spent * 0.8 + np.random.randint(-50, 50, 50)

fig = px.scatter(
    x=ad_spent,
    y=sales,
    labels={"x": "Затраты на рекламу", "y": "Продажи"},
    title="Зависимость продаж от затрат на рекламу"
)
fig.update_layout(template="plotly_white", font=dict(size=14))
fig.show()
Диаграмма рассеяния

Средний уровень

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

Комбинированные графики (Subplots)

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

Тип графика
Используем subplots (комбинацию нескольких графиков в одном поле) для более емкой презентации данных.

Пример кода

import plotly.graph_objs as go
from plotly.subplots import make_subplots

months = ["Янв", "Фев", "Мар", "Апр", "Май", "Июн"]
sales = [1500, 1800, 2100, 1600, 2400, 2200]
profit = [400, 500, 600, 380, 750, 670]

# Создаем сетку из 1 строки и 2 столбцов
fig = make_subplots(rows=1, cols=2, subplot_titles=["Продажи", "Прибыль"])

# Первый график (столбчатая диаграмма для продаж)
fig.add_trace(
    go.Bar(x=months, y=sales, name="Продажи"),
    row=1, col=1
)

# Второй график (линейный график для прибыли)
fig.add_trace(
    go.Scatter(x=months, y=profit, mode="lines+markers", name="Прибыль"),
    row=1, col=2
)

fig.update_layout(
    title="Анализ продаж и прибыли",
    template="plotly_white",
    font=dict(size=14)
)

fig.show()
Комбинированные графики

Группированные столбчатые диаграммы и столбчатые диаграммы с накоплением (Grouped / Stacked Bars)

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

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

Пример кода (Группированные столбчатые диаграммы)

import plotly.graph_objs as go

regions = ["Север", "Юг", "Восток", "Запад"]
product_A = [150, 200, 130, 100]
product_B = [100, 150, 180, 90]

fig = go.Figure()
fig.add_trace(go.Bar(x=regions, y=product_A, name="Товар A"))
fig.add_trace(go.Bar(x=regions, y=product_B, name="Товар B"))

fig.update_layout(
    barmode='group',  # Группированные столбцы
    title="Продажи товаров A и B по регионам",
    xaxis_title="Регион",
    yaxis_title="Продажи",
    template="plotly_white",
    font=dict(size=14)
)

fig.show()
Групированная столбчатая диаграмма

Пример кода (Столбчатые диаграммы с накоплением)

regions = ["Север", "Юг", "Восток", "Запад"]
product_A = [150, 200, 130, 100]
product_B = [100, 150, 180, 90]

fig = go.Figure()
fig.add_trace(go.Bar(x=regions, y=product_A, name="Товар A"))
fig.add_trace(go.Bar(x=regions, y=product_B, name="Товар B"))

fig.update_layout(
    barmode='stack',  # Накопленные столбцы
    title="Продажи товаров A и B по регионам (диаграмма с накоплением)",
    xaxis_title="Регион",
    yaxis_title="Продажи",
    template="plotly_white",
    font=dict(size=14)
)

fig.show()
Диаграмма с накоплением

Ящик с усами и скрипичная диаграмма (Box Plot и Violin Plot)

Описание задачи

  • Ящик с усами (Box Plot) и скрипичная диаграмма (Violin Plot) позволяют детально изучить распределение данных, выявлять выбросы, отображать медиану, квартили и т.д.
  • Пример: анализ зарплат сотрудников в разных департаментах.

Тип графика
Подходит для анализа распределений по категориям, оценки смещения данных, выбросов.

Пример кода (Ящик с усами)

import plotly.express as px
import numpy as np

dept_A = np.random.normal(50000, 5000, 200)
dept_B = np.random.normal(52000, 6000, 200)
dept_C = np.random.normal(48000, 4000, 200)

data = {
    "Зарплата": list(dept_A) + list(dept_B) + list(dept_C),
    "Департамент": ["A"] * 200 + ["B"] * 200 + ["C"] * 200
}

fig = px.box(
    data,
    x="Департамент",
    y="Зарплата",
    title="Распределение зарплат по департаментам",
    points="all"  
)
fig.update_layout(template="plotly_white", font=dict(size=14))
fig.show()
Ящик с усами

Пример кода (Скрипичная диаграмма)

fig = px.violin(
    data,
    x="Департамент",
    y="Зарплата",
    box=True,       
    points="all",
    title="Распределение зарплат по департаментам (Скрипичная диаграмма)"
)
fig.update_layout(template="plotly_white", font=dict(size=14))
fig.show()
Скрипичная диаграмма

Тепловая карта (Heatmap)

Описание задачи
Отобразить интенсивность показателя в двумерной матрице (например, корреляционная матрица признаков).

Тип графика
Тепловые карты позволяют визуально определить зоны высокой или низкой концентрации, выявить паттерны.

Пример кода

import numpy as np
import pandas as pd
import plotly.graph_objs as go

n = 120

sales       = np.random.normal(loc=3000, scale=800, size=n)               # Продажи
ad_spend    = sales * 0.3 + np.random.normal(scale=100, size=n)          # Расходы на рекламу
profit      = sales * 0.15 + np.random.normal(scale=50, size=n)          # Прибыль
clients     = np.random.poisson(lam=70, size=n)                           # Количество клиентов
satisfaction = np.random.uniform(low=1, high=5, size=n)                  # Удовлетворённость

df = pd.DataFrame({
    "Продажи, тыс. ₽":        sales,
    "Реклама, тыс. ₽":        ad_spend,
    "Прибыль, тыс. ₽":        profit,
    "Клиенты, чел.":          clients,
    "Удовлетворенность, балл": satisfaction
})

corr = df.corr().round(2) # Корреляционная матрица

fig = go.Figure(
    data=go.Heatmap(
        z=corr.values,
        x=corr.columns,
        y=corr.index,
        colorscale='Purples',     
        zmin=-1, zmax=1,
        colorbar=dict(title="r"),
        text=corr.values,
        texttemplate="%{text}",
        textfont=dict(color="white", size=12)
    )
)

fig.update_layout(
    title="Корреляция ключевых бизнес‑метрик",
    xaxis_title="Метрика",
    yaxis_title="Метрика",
    template="plotly_white",
    font=dict(size=14),
    margin=dict(l=120, r=120, t=100, b=100)
)

fig.show()
Тепловая карта

Продвинутый уровень

На этом уровне рассмотрим нестандартные возможности Plotly: географические карты, 3D-графики, динамические (анимированные) визуализации, а также настройку интерактивных элементов (слайдеров, кнопок).

Географические карты (Choropleth, Scatter Geo)

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

Тип графика

  • Choropleth — закрашивание областей (стран/регионов) на карте по интенсивности показателя.
  • Scatter Geo — точки/маркеры на карте.

Пример кода (Choropleth с данными о населении стран)

import pandas as pd
import plotly.express as px

df = px.data.gapminder()

fig = px.choropleth(
    df,
    locations="iso_alpha",            # Колонка с ISO-3 кодом в df
    color="pop",
    color_continuous_scale="Purp", 
    hover_name="country",
    labels={"pop": "Население"},      
    projection="natural earth",
    animation_frame='year',
    title="Население стран мира"
)


fig.update_layout(
    template="plotly_white",
    font=dict(size=14),
    margin=dict(l=20, r=20, t=60, b=20)
)

fig.show()
Интерактивный график (Choropleth с данными о населении стран)

Пример кода (Scatter Geo с данными о населении стран)

df = px.data.gapminder()

fig = px.scatter_geo(
    df,
    locations="iso_alpha",            # Колонка с ISO-3 кодом в df
    color="pop",
    color_continuous_scale="Purp", 
    hover_name="country",
    labels={"pop": "Население"},      
    projection="natural earth",
    animation_frame='year',
    title="Население стран мира"
)


fig.update_layout(
    template="plotly_white",
    font=dict(size=14),
    margin=dict(l=20, r=20, t=60, b=20)
)

fig.show()
Интерактивный график (Scatter Geo с данными о населении стран)

3D-графики (3D Scatter, 3D Surface)

Описание задачи
Отобразить многомерные данные (три оси + цвет/размер), или построить 3D-поверхность для наглядного представления функции двух переменных.

Задача: оценить, как меняются основные параметры квартир на рынке Москвы:

  • Площадь, м²
  • Цена за м², ₽
  • Удаленность от центра, км

Пример кода (3D Scatter)

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

# Генерация примерных данных
np.random.seed(42)
n = 100

# Площадь от 30 до 120 м²
area = np.random.uniform(30, 120, n)

# Расстояние до центра Москвы
distance = np.random.exponential(scale=8, size=n)
distance = np.clip(distance, 1, 30)

# Цена за м²
price_per_m2 = (
    200_000 
    - distance * 3_000 
    + np.random.normal(scale=10_000, size=n)
)
price_per_m2 = np.clip(price_per_m2, 80_000, 300_000)

df = pd.DataFrame({
    "Площадь, м²": area,
    "Удаленность, км": distance,
    "Цена за м², ₽": price_per_m2
})

fig = px.scatter_3d(
    df,
    x="Площадь, м²",
    y="Удаленность, км",
    z="Цена за м², ₽",
    color="Цена за м², ₽",          # Раскрашиваем по цене
    labels={
        "Площадь, м²": "Площадь (м²)",
        "Удаленность, км": "Расстояние от центра (км)",
        "Цена за м², ₽": "Цена за кв.м (₽)"
    },
    title="3D-анализ рынка квартир Москвы",
    color_continuous_scale=px.colors.sequential.Purp
)

fig.update_layout(
    template="plotly_white",
    font=dict(size=12),
    margin=dict(l=0, r=0, t=50, b=0)
)

fig.show()
3D Scatter

Пример кода (3D Surface)

import numpy as np
import plotly.graph_objs as go

# Диапазоны площадей и дистанций
x = np.linspace(30, 120, 50)   # площадь от 30 до 120 м²
y = np.linspace(1, 30, 50)     # расстояние от центра от 1 до 30 км

# Сетка
X, Y = np.meshgrid(x, y)

# Цена за м²:
Z = 300_000 - 1_000 * X - 2_000 * Y

fig = go.Figure(
    data=[go.Surface(
        x=X,
        y=Y,
        z=Z,
        colorscale='Purples',
        cmin=Z.min(),
        cmax=Z.max(),
        colorbar=dict(title="Цена за м², ₽")
    )]
)

fig.update_layout(
    title="3D Surface: Цена за м² в зависимости от площади и удаленности",
    scene=dict(
        xaxis=dict(title="Площадь, м²"),
        yaxis=dict(title="Удаленность от центра, км"),
        zaxis=dict(title="Цена за м², ₽")
    ),
    template="plotly_white",
    font=dict(size=12),
    margin=dict(l=50, r=50, t=80, b=50)
)

fig.show()
3D Surface

Анимация в Plotly

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

Тип графика
Анимированные графики (animated scatter, animated bar и др.) привлекают внимание и упрощают понимание динамики.

Пример кода

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

regions = [
    "Москва", "Санкт-Петербург", "Новосибирская обл.",
    "Краснодарский край", "Республика Татарстан", "Свердловская обл.",
    "Брянская обл."                  
]
district_map = {
    "Москва": "Центральный",
    "Санкт-Петербург": "Северо-Западный",
    "Новосибирская обл.": "Сибирский",
    "Краснодарский край": "Южный",
    "Республика Татарстан": "Приволжский",
    "Свердловская обл.": "Уральский",
    "Брянская обл.": "Центральный"    
}

years = list(range(2015, 2023))

# Примерные значения по регионам
base_pop = {
    "Москва": 12e6,
    "Санкт-Петербург": 5.3e6,
    "Новосибирская обл.": 2.8e6,
    "Краснодарский край": 5.6e6,
    "Республика Татарстан": 3.9e6,
    "Свердловская обл.": 4.3e6,
    "Брянская обл.": 1.2e6          
}
base_salary = {
    "Москва": 65000,
    "Санкт-Петербург": 55000,
    "Новосибирская обл.": 40000,
    "Краснодарский край": 38000,
    "Республика Татарстан": 42000,
    "Свердловская обл.": 43000,
    "Брянская обл.": 35000           
}
base_unemp = {
    "Москва": 4.0,
    "Санкт-Петербург": 4.5,
    "Новосибирская обл.": 5.5,
    "Краснодарский край": 6.0,
    "Республика Татарстан": 5.0,
    "Свердловская обл.": 5.2,
    "Брянская обл.": 6.5            
}

# Генерация данных
data = []
for year in years:
    for reg in regions:
        pop = base_pop[reg] * (1 + 0.005 * (year - 2015))
        salary = base_salary[reg] * (1 + 0.03 * (year - 2015)) \
                 + np.random.normal(scale=2000)
        unemp = base_unemp[reg] - 0.1 * (year - 2015) \
                + np.random.normal(scale=0.2)
        data.append({
            "Регион": reg,
            "Округ": district_map[reg],
            "Год": year,
            "Население": max(pop, 0),
            "Зарплата, ₽": salary,
            "Безработица, %": max(unemp, 0)
        })

df = pd.DataFrame(data)

fig = px.scatter(
    df,
    x="Зарплата, ₽",
    y="Безработица, %",
    animation_frame="Год",
    animation_group="Регион",
    size="Население",
    color="Округ",
    hover_name="Регион",
    log_x=True,
    range_x=[30000, 90000],
    range_y=[2, 8],
    size_max=60,
    labels={
        "Зарплата, ₽": "Среднемесячная зарплата (₽)",
        "Безработица, %": "Уровень безработицы (%)",
        "Население": "Население региона",
        "Округ": "Федеральный округ"
    },
    title="Динамика зарплаты и безработицы по крупным регионам РФ (2015–2022)"
)


fig.show()
Анимированная диаграмма рассения

Дополнительные графики и возможности

Собрали еще несколько интересных вариантов визуализаций, которые могут понадобиться в BI-проектах.

Воронка (Funnel Chart)

Описание задачи
Проанализировать эффективность трех основных каналов привлечения пользователей на мобильное приложение:

  • Контекстная реклама
  • Социальные сети
  • Email-рассылки

На каждом этапе воронки теряется часть аудитории. Задача — сравнить, в каком канале и на каком шаге «проседает» конверсия.

Пример кода

import pandas as pd
import plotly.express as px

channels = ["Контекстная реклама"] * 6 + ["Социальные сети"] * 6 + ["Email-рассылка"] * 6
stages   = ["Показы объявления", "Клики", "Установки приложения",
            "Регистрация", "Первое действие", "Оплата подписки"] * 3
values   = [
    # Контекстная реклама
    10_000,  8_000,  3_200,  2_400,  1_600,  2_100,
    # Социальные сети
     8_300,  5_500,  2_500,  1_800,  1_200,    1_800,
    # Email-рассылка
     2_700,  4_100,  1_200,    900,    600,    1_400
]

df = pd.DataFrame({
    "Канал":      channels,
    "Этап":       stages,
    "Конверсии":  values
})

# Множество воронок
fig = px.funnel(
    df,
    x="Конверсии",
    y="Этап",
    color="Канал",
    title="Воронка конверсии по каналам привлечения",
    labels={"Конверсии": "Количество пользователей", "Этап": "Шаг воронки"}
)
fig.update_traces(
    texttemplate="%{x:.2s}",  
    textposition="inside"
)

fig.update_layout(
    xaxis=dict(
        tickformat=".2s"  
    ),
    template="plotly_white",
    font=dict(size=13),
    legend_title_text="Канал",
  
)

fig.show()
Воронка

Солнечные лучи (Sunburst) и дерево (Treemap)

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

  • Какие категории приносят больше всего выручки.
  • Внутри категории, какие подкатегории лидируют.
  • На уровне брендов — распределение продаж.

С помощью Sunburst и Treemap есть возможность сразу погрузиться в каждый уровень и увидеть доли и абсолютные значения.

Пример кода

import pandas as pd
import plotly.express as px

data = [
    ("Электроника", "Смартфоны",  "Apple",    120), 
    ("Электроника", "Смартфоны",  "Samsung",  90),
    ("Электроника", "Смартфоны",  "Xiaomi",   60),
    ("Электроника", "Ноутбуки",   "Dell",     50),
    ("Электроника", "Ноутбуки",   "HP",       40),
    ("Электроника", "Ноутбуки",   "Lenovo",   30),
    ("Одежда",     "Мужская",     "MAAG",     80),
    ("Одежда",     "Мужская",     "Nike",     70),
    ("Одежда",     "Женская",     "12 STOREEZ", 65),
    ("Одежда",     "Женская",     "Lime",   55),
    ("Дом и сад",  "Мебель",      "Leroy Merlin", 95),
    ("Дом и сад",  "Мебель",      "Hoff",     45),
    ("Дом и сад",  "Текстиль",    "Tchibo",   35),
    ("Дом и сад",  "Текстиль",    "Leroy Merlin", 25),
]

df = pd.DataFrame(data, columns=["Категория", "Подкатегория", "Бренд", "Продажи, млн ₽"])

fig = px.sunburst(
    df,
    path=["Категория", "Подкатегория", "Бренд"],
    values="Продажи, млн ₽",
    color="Категория",
    color_discrete_map={
        "Электроника": "#636EFA",
        "Одежда":       "#EF553B",
        "Дом и сад":    "#00CC96"
    },
    title="Структура продаж по категориям, подкатегориям и брендам",
    labels={"Продажи, млн ₽":"Выручка (млн ₽)"}
)
fig.update_traces(
    hovertemplate="<b>%{label}</b><br>Выручка: %{value} млн ₽<br>Доля: %{percentRoot:.1%}",
    textinfo="label+percent entry"
)
fig.update_layout(template="plotly_white", font=dict(size=13))
fig.show()
Солнечные лучи (Sunburst)
fig = px.treemap(
    df,
    path=["Категория", "Подкатегория", "Бренд"],
    values="Продажи, млн ₽",
    color="Категория",
    color_discrete_map={
        "Электроника": "#636EFA",
        "Одежда":       "#EF553B",
        "Дом и сад":    "#00CC96"
    },
    title="Древовидная карта продаж по категориям и брендам",
    labels={"Продажи, млн ₽":"Выручка (млн ₽)"}
)
fig.update_traces(
    hovertemplate="<b>%{label}</b><br>Выручка: %{value} млн ₽<br>Доля внутри: %{percentParent:.1%}",
    texttemplate="%{label}<br>%{value} млн"
)
fig.update_layout(margin=dict(l=20, r=20, t=60, b=20),
                   template="plotly_white", font=dict(size=13))
fig.show()
Дерево (Treemap)

Диаграмма Ганта (Gantt chart)

Описание
План разработки и запуска нового веб-сервиса. Необходимо наглядно отобразить:

  • Этапы от сбора требований до финального деплоя.
  • Задействованные команды (аналитика, дизайн, фронтенд, бэкенд, QA, DevOps).
  • Ключевые этапы.

Пример кода

import pandas as pd
import plotly.express as px
import plotly.graph_objects as go

df = pd.DataFrame([
    {"Этап": "Сбор требований",           "Start": "2025-01-05", "Finish": "2025-01-15", "Команда": "Аналитика"},
    {"Этап": "Дизайн интерфейса",         "Start": "2025-01-10", "Finish": "2025-01-25", "Команда": "Дизайн"},
    {"Этап": "Разработка бэкенда",        "Start": "2025-01-20", "Finish": "2025-02-20", "Команда": "Бэкенд"},
    {"Этап": "Разработка фронтенда",      "Start": "2025-01-25", "Finish": "2025-02-28", "Команда": "Фронтенд"},
    {"Этап": "Интеграция компонентов",    "Start": "2025-02-15", "Finish": "2025-03-05", "Команда": "Интеграция"},
    {"Этап": "Тестирование API",          "Start": "2025-03-01", "Finish": "2025-03-10", "Команда": "QA"},
    {"Этап": "Тестирование UI",           "Start": "2025-03-05", "Finish": "2025-03-15", "Команда": "QA"},
    {"Этап": "Деплой и запуск",           "Start": "2025-03-16", "Finish": "2025-03-20", "Команда": "DevOps"},
])

# Диаграмма Ганта через px.timeline
fig = px.timeline(
    df,
    x_start="Start",
    x_end="Finish",
    y="Этап",
    color="Команда",
    title="План проекта: разработка и запуск веб-сервиса",
    labels={
        "Start": "Начало",
        "Finish": "Окончание",
        "Этап": "Этап проекта"
    },
    color_discrete_map={
        "Аналитика":    "#636EFA",
        "Дизайн":       "#EF553B",
        "Бэкенд":       "#00CC96",
        "Фронтенд":     "#AB63FA",
        "Интеграция":   "#FFA15A",
        "QA":           "#19D3F3",
        "DevOps":       "#FF6692"
    }
)

fig.update_yaxes(autorange="reversed")

# Ключевые этапы с аннотациями
milestones = {
    "2025-01-15": "Утверждена спецификация",
    "2025-02-20": "Завершен бэкенд",
    "2025-03-05": "Готова интеграция",
    "2025-03-20": "Выход в продакшн"
}
for date, label in milestones.items():
    fig.add_vline(
        x=date, line_dash="dot", line_color="gray", opacity=0.7
    )
    fig.add_annotation(
        x=date, y=-0.5, text=label, showarrow=False,
        yshift=10, font=dict(size=11, color="gray")
    )

fig.update_layout(
    template="plotly_white",
    font=dict(size=12),
    margin=dict(l=150, r=20, t=80, b=80),
    legend_title_text="Команда",
    hoverlabel_align="left"
)

fig.show()
Диаграмма Ганта

Интерактивные элементы: слайдеры, кнопки

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

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

Сценарий
Компания хочет посмотреть, как менялись квартальные продажи в разных федеральных округах за период 2018–2022 гг.
– С помощью слайдера выбирается год.
Кнопки переключают режим отображения столбцов: «Группированные» / «Накопленные»

Пример кода

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

np.random.seed(0)
years    = list(range(2018, 2023))
quarters = ['1-й квартал\n(янв–март)', 
            '2-й квартал\n(апр–июнь)', 
            '3-й квартал\n(июль–сентябрь)', 
            '4-й квартал\n(окт–дек)']
districts = ['Центральный', 'Приволжский', 'Уральский', 'Сибирский', 'Южный']

data = []
for year in years:
    for q in quarters:
        for d in districts:
            sales = np.random.uniform(100, 300)  # млн ₽
            data.append({
                'Год': year,
                'Квартал': q,
                'Округ': d,
                'Продажи, млн ₽': round(sales, 1)
            })
df = pd.DataFrame(data)

# Базовый px.bar с анимацией по годам 
fig = px.bar(
    df,
    x='Квартал',
    y='Продажи, млн ₽',
    color='Округ',
    animation_frame='Год',
    category_orders={'Квартал': quarters},
    labels={
        'Квартал': 'Квартал',
        'Продажи, млн ₽': 'Продажи, млн ₽',
        'Округ': 'Федеральный округ',
        'Год': 'Год'
    },
    title='Квартальные продажи по федеральным округам РФ (2018–2022)',
    barmode='group',        # по умолчанию группированные
)

# Кнопки для смены режима отображения столбцов
fig.update_layout(
    updatemenus=[dict(
        type = "buttons",
        direction = "right",
        x=0.1, y=1.12,
        buttons=list([
            dict(
                label="Группированные столбцы",
                method="relayout",
                args=[{"barmode": "group"}]
            ),
            dict(
                label="Накопленные столбцы",
                method="relayout",
                args=[{"barmode": "stack"}]
            ),
        ]),
        showactive=True
    )]
    
)

fig.update_layout(
    template="plotly_white",
    font=dict(size=12),
)

fig.show()
Отображение группированных столбцов
Отображение столбцов с накоплением

Когда Plotly Express, а когда Graph Objects

Задача
Понять, какой API выбрать — упрощенный px или go — и как их сочетать.

Подход

  • px.* подходит для быстрого построения типовых графиков.
  • go.Figure дает полный контроль над трейсам, осями, аннотациями.
  • Можно строить с помощью px, а затем править через fig.update_traces() / fig.update_layout().

Задача

Сравнить два ключевых показателя интернет-магазина по месяцам:

  • Количество заказов
  • Средний чек (в руб.)

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

Пример кода

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

# Генерация данных
np.random.seed(42)
rus_months = ["Янв", "Фев", "Мар", "Апр", "Май", "Июн",
              "Июл", "Авг", "Сен", "Окт", "Ноя", "Дек"]
dates = pd.date_range("2024-01-01", periods=12, freq="MS")
months = [rus_months[d.month - 1] for d in dates]

df = pd.DataFrame({
    "Месяц": months,
    "Заказы": np.random.randint(200, 500, size=12),
    "Средний чек, ₽": np.random.uniform(1500, 3000, size=12).round(0)
})

# Быстрое построение графика через Plotly Express
fig = px.bar(
    df,
    x="Месяц",
    y="Заказы",
    title="Динамика заказов и среднего чека в 2024 г.",
    labels={"Заказы": "Кол-во заказов", "Месяц": "Месяц"},
    template="plotly_white"
)

# Добавление линии среднего чека 
fig.add_trace(
    go.Scatter(
        x=df["Месяц"],
        y=df["Средний чек, ₽"],
        mode="lines+markers",
        name="Средний чек, ₽",
        yaxis="y2",
        marker=dict(size=8),
        line=dict(dash="dash")
    )
)

fig.update_layout(
    xaxis_title="Месяц",
    yaxis=dict(
        title="Кол-во заказов",
        showgrid=True
    ),
    yaxis2=dict(
        title="Средний чек, ₽",
        overlaying="y",
        side="right",
        showgrid=False
    ),
    legend=dict(y=1.1, orientation="h"),
    margin=dict(t=80, b=40, l=60, r=60)
)

fig.show()
Использование px и go

Оформление

Понятный шаблон:

  • template=«plotly_white» (или plotly_dark, ggplot2 и др.), чтобы график выглядел аккуратно.

Масштаб шрифта и читаемость:

  • font=dict(size=12) или больше, чтобы надписи были хорошо видны.

Подписи осей и легенды:

  • Указывайте xaxis_title и yaxis_title, понятный заголовок title.

Всплывающие подсказки (hover):

  • Уточняйте, какие именно данные показывать. Можно кастомизировать с помощью hovertemplate.

Подсветка важных элементов:

  • Настраивайте цвета через аргументы line, marker и т.д.