2025, Nov 05 12:01
Итоги без искажений: исключаем столбец итогов из теплокарты Plotly
Разберём, как сделать теплокарту Plotly с колонкой итогов, не искажающей цвета: исключаем grand_total из раскраски, оставляем числа в тексте, go.Heatmap.
Тепловые карты Plotly отлично подходят для визуализации сводных метрик, но часто всплывает одна ловушка, когда вы добавляете столбец итогов по строкам. Этот единственный столбец попадает в цветовую шкалу и «перебивает» остальные ячейки, из‑за чего они выглядят блеклыми и малоинформативными. Задача — оставить итоги на виду, но исключить их из раскраски.
Постановка задачи
Рассмотрим сводную таблицу pandas, где мы добавляем столбец итогов и пытаемся построить её с помощью Plotly Express. Итоги полезны как контекст, но они тоже окрашиваются и тем самым искажают шкалу.
import numpy as np
import pandas as pd
import plotly.express as px
origin_cities = ['London', 'Tokio', 'Seoul', 'Paris', 'Tashkent', 'Washington', 'Moscow']
current_cities = ['London', 'Madrid', 'Tashkent', 'Seoul', 'Paris', 'Toronto', 'Washington', 'Istanbul', 'Hanoi', 'Manilla', 'Delhi', 'Busan', 'Moscow']
src_city = np.random.choice(origin_cities, size=1000)
dst_city = np.random.choice(current_cities, size=1000)
pay = np.random.randint(1300, 6900, size=1000)
base_df = pd.DataFrame({'src_city': src_city, 'dst_city': dst_city, 'pay': pay})
wide_tbl = pd.pivot_table(
base_df,
index='src_city',
columns='dst_city',
values='pay',
aggfunc='sum',
fill_value=0
)
wide_tbl['grand_total'] = wide_tbl.sum(axis=1)
col_order = ['grand_total'] + [c for c in wide_tbl.columns if c != 'grand_total']
wide_tbl = wide_tbl[col_order]
px.imshow(wide_tbl, text_auto=True)
В чём проблема на самом деле
Тепловая карта использует все переданные значения для расчёта и применения цветовой шкалы. Когда итоги стоят рядом с обычными ячейками, они влияют на эту шкалу. В результате цвета уже не отражают распределение, которое вы хотели подчеркнуть в неитоговых столбцах.
Решение
Лекарство — разделить то, что окрашивается, и то, что показывается как текст. Создайте один DataFrame для значений теплокарты и установите в столбце итогов значение None, чтобы он не получал цвет. Отдельно подготовьте DataFrame для текстовых подписей, чтобы итоги по‑прежнему отображались числом. Поскольку этот подход опирается на go.Heatmap, нужно передавать массивы и перевернуть порядок строк: go.Heatmap рисует первую строку массива внизу теплокарты. Так как столбец итогов становится прозрачным, отключение линий сетки помогает избежать визуального шума; при желании можно подправить фон или тему, чтобы столбец итогов читался иначе.
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
origin_cities = ['London', 'Tokio', 'Seoul', 'Paris', 'Tashkent', 'Washington', 'Moscow']
current_cities = ['London', 'Madrid', 'Tashkent', 'Seoul', 'Paris', 'Toronto', 'Washington', 'Istanbul', 'Hanoi', 'Manilla', 'Delhi', 'Busan', 'Moscow']
np.random.seed(42)
src_city = np.random.choice(origin_cities, size=1000)
dst_city = np.random.choice(current_cities, size=1000)
pay = np.random.randint(1300, 6900, size=1000)
base_df = pd.DataFrame({'src_city': src_city, 'dst_city': dst_city, 'pay': pay})
wide_tbl = pd.pivot_table(
base_df,
index='src_city',
columns='dst_city',
values='pay',
aggfunc='sum',
fill_value=0
)
wide_tbl['grand_total'] = wide_tbl.sum(axis=1)
col_order = ['grand_total'] + [c for c in wide_tbl.columns if c != 'grand_total']
wide_tbl = wide_tbl[col_order]
# значения для раскраски: скрываем итоги, присваивая им None
heat_vals = wide_tbl.iloc[::-1].copy()
heat_vals['grand_total'] = None
# текст для отображения: сохраняем все значения как строки
heat_text = wide_tbl.iloc[::-1].astype(str).copy()
z_data = heat_vals.to_numpy()
text_data = heat_text.to_numpy()
y_labels = heat_text.index.values
x_labels = heat_text.columns.values
fig = go.Figure()
fig.add_trace(go.Heatmap(
z=z_data,
y=y_labels,
x=x_labels,
text=text_data,
texttemplate="%{text:.2s}",
))
fig.update_layout(
xaxis=dict(showgrid=False),
yaxis=dict(showgrid=False),
)
fig.show()
Почему это важно
Аналитические теплокарты часто должны сочетать цветовой градиент и контекстные итоги. Если дать итогам управлять цветовой шкалой, сравнение между обычными ячейками теряет смысл. Разделив данные для окраски и для отображения, вы сохраняете читаемость, не жертвуя контекстом.
Итоги
Если столбец итогов «перекашивает» вашу теплокарту Plotly, оставьте числа видимыми как текст, а из z‑значений исключите их, присваивая None. Переключитесь на go.Heatmap, передайте массивы значений и текста, переверните порядок строк для ожидаемого отображения и отключите линии сетки для более чистого вида. Небольшой рефакторинг сохранит осмысленную цветовую шкалу и покажет итоги именно там, где аналитики привыкли их видеть.