查看全课大纲(第 24 / 45 节)

热力图

小象实战讲义 · ECharts 数据可视化

热力图是一种通过颜色深浅来展示二维数据矩阵中第三维数值大小的图表。它非常适合用于观察数据的密度分布、模式识别以及行与列之间的对比关系,例如网站点击热图、一周内各时段活动量分析等。本节将学习热力图的原理、数据格式要求,并使用 pyecharts 创建可交互的热力图。

💡 核心导读

  • 图表原理:热力图通过二维坐标(行与列)定位矩形色块,并用颜色映射(visualMap)表示该位置上的数值大小。
  • 数据格式:核心数据是一个列表,其中每个元素是 [x索引, y索引, 数值] 的三元组。
  • 关键配置:使用 visualmap_opts 定义数值到颜色的映射关系;使用 label_opts 控制是否在色块内显示具体数值。
  • 应用场景:适用于展示矩阵数据(如时间-类别、地理位置-指标)的密度与强度,直观呈现“哪里更热(值更大)”。

1. 理解热力图的数据关系

热力图是一种将第三维信息用颜色进行编码的图表。它通常由两维信息来确定每个矩形色块的位置(例如行和列),而第三维信息(数值)则通过视觉映射器(VisualMap)转换为颜色。

一个典型的例子是分析一周(7天)内每小时(24小时)的活动量:

  • 横轴(X轴):代表一天的24个小时。
  • 纵轴(Y轴):代表从周一到周日。
  • 每个交叉点(单元格):代表一个特定的时间段(如周一12点),其对应的数值(如活动量29)决定了该单元格的颜色。

这样,我们就得到了一个 24(小时) × 7(天) = 168 个数据点的矩阵。通过颜色的深浅,我们可以一眼看出哪些时间段的活动最为“热烈”。

2. 准备热力图的数据

热力图要求的数据格式是一个列表,列表中的每个元素都是一个三元组:[x索引, y索引, 数值]。这里的两个索引都是整数,分别对应横轴和纵轴上的位置。

下面我们生成一个确定性的示例数据,模拟一周内每小时的活动量(0-50之间的整数)。

# 导入必要的库
from pyecharts import options as opts
from pyecharts.charts import HeatMap
import random

# 固定随机种子,确保每次生成的数据相同
random.seed(2024)

# 生成热力图数据:格式为 [x索引(小时), y索引(星期几), 数值]
# x索引 i: 0-23 代表 0点到23点
# y索引 j: 0-6 代表 周一到周日
data = [
    [i, j, random.randint(0, 50)]
    for i in range(24)
    for j in range(7)
]

# 查看生成的数据结构:总共有 24*7=168 条记录
print(f"数据总条数: {len(data)}")
print("前5条数据示例:")
for item in data[:5]:
    print(item)
数据总条数: 168
前5条数据示例:
[0, 0, 30]
[0, 1, 11]
[0, 2, 46]
[0, 3, 37]
[0, 4, 19]

数据解读:第一条 [0, 0, 30] 表示横轴第0项(0点)、纵轴第0项(星期一)的数值是30。

3. 绘制基础热力图

有了数据后,我们需要定义坐标轴的实际标签(而不是显示索引数字),并调用 HeatMap 进行绘制。

# 本代码块自包含,可单独运行
import random
from pyecharts import options as opts
from pyecharts.charts import HeatMap

random.seed(2024)
data = [[hour, day, random.randint(0, 50)] for hour in range(24) for day in range(7)]
x_axis_labels = [f"{hour}点" for hour in range(24)]
y_axis_labels = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"]

heatmap = (
    HeatMap()
    .add_xaxis(x_axis_labels)
    .add_yaxis(
        series_name="活动量",
        yaxis_data=y_axis_labels,
        value=data,
        label_opts=opts.LabelOpts(is_show=False),
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title="一周各时段活动量热力图"),
        visualmap_opts=opts.VisualMapOpts(
            min_=0,
            max_=50,
            is_calculable=True,
            orient="horizontal",
            pos_left="center",
        ),
    )
)

print("图表配置摘要:")
print(f"  X轴刻度数: {len(x_axis_labels)}")
print(f"  Y轴刻度数: {len(y_axis_labels)}")
print(f"  数据点数: {len(data)}")
print(f"  图表类名: {type(heatmap).__name__}")
图表配置摘要:
  X轴刻度数: 24
  Y轴刻度数: 7
  数据点数: 168
  图表类名: HeatMap

此时,一个基础的热力图就配置完成了。图表中,颜色越偏向红色表示数值越接近50(活动量高),越偏向蓝色表示数值越接近0(活动量低)。你可以通过底部的颜色滑块交互地过滤查看特定数值区间的数据。

4. 优化热力图:显示数值与解读

基础热力图通过颜色传达了整体趋势。有时我们还需要看到精确数值,这可以通过开启标签(Label)来实现。同时,我们学习如何从生成的热力图中解读信息。

# 本代码块自包含,可单独运行
import random
from pyecharts import options as opts
from pyecharts.charts import HeatMap

random.seed(2024)
data = [[hour, day, random.randint(0, 50)] for hour in range(24) for day in range(7)]
x_axis_labels = [f"{hour}点" for hour in range(24)]
y_axis_labels = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"]

heatmap_optimized = (
    HeatMap()
    .add_xaxis(x_axis_labels)
    .add_yaxis(
        series_name="活动量",
        yaxis_data=y_axis_labels,
        value=data,
        label_opts=opts.LabelOpts(is_show=True, position="inside", color="#000"),
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title="一周各时段活动量热力图(带数值)"),
        visualmap_opts=opts.VisualMapOpts(
            min_=0,
            max_=50,
            is_calculable=True,
            orient="horizontal",
            pos_left="center",
            range_color=["#313695", "#4575b4", "#74add1", "#abd9e9", "#fee090", "#fdae61", "#f46d43", "#d73027", "#a50026"],
        ),
    )
)

max_item = max(data, key=lambda item: item[2])
hour, weekday, value = max_item
print("优化后图表摘要:")
print(f"  数据点数: {len(data)}")
print(f"  最大值: {value}")
print(f"  最大值位置: {y_axis_labels[weekday]}的{hour}点")
一周七天二十四小时活动量热力图,色块越深表示活动量越高
图:一周×24小时活动量热力图(开启数值标签)。重点看颜色深浅与星期二的8点高值。手机端可左右滑动查看,点击图片可看全图。
优化后图表摘要:
  数据点数: 168
  最大值: 50
  最大值位置: 星期二的8点

图表解读:

  • 颜色观察:即使不看具体数字,颜色最红(或最接近定义的高值颜色)的色块也代表了活动量的高峰。
  • 模式识别:你可以观察是否有整行(某一天)或整列(某个小时段)颜色普遍较深,这反映了天或时段的整体活跃模式。
  • 交互探索:拖动视觉映射滑块,可以动态过滤掉低值或高值区域,专注于特定强度区间的数据分布。

📝 动手练一练

  1. 修改颜色映射:将上面代码中 visualmap_opts 的 range_color 改为 ["#f7fbff", "#08306b"](从浅蓝到深蓝),观察图表风格的变化。思考在什么业务场景下适合使用单色系渐变。
点击查看参考答案
# 只需修改 set_global_opts 中的 visualmap_opts 部分
visualmap_opts=opts.VisualMapOpts(
    min_=0,
    max_=50,
    is_calculable=True,
    orient="horizontal",
    pos_left="center",
    range_color=["#f7fbff", "#08306b"]  # 改为蓝白单色系渐变
)

场景思考:单色系渐变(如蓝色渐变)适合表示同一指标的正向强度,如温度、密度、压力等,避免因多色带来的“冷热”联想误导。在需要严谨、客观呈现单一维度数据时常用。

  1. 数据转换练习:假设你有一个 pandas DataFrame,结构如下,请将其转换为热力图所需的 [x索引, y索引, 数值] 格式列表。
    import pandas as pd
    import numpy as np
    # 模拟数据
    np.random.seed(42)
    df = pd.DataFrame(np.random.randint(0, 100, size=(5, 4)),
                      index=['产品A', '产品B', '产品C', '产品D', '产品E'],
                      columns=['Q1', 'Q2', 'Q3', 'Q4'])

<details>
<summary> 点击查看参考答案</summary>

```python
import pandas as pd
import numpy as np

np.random.seed(42)
df = pd.DataFrame(np.random.randint(0, 100, size=(5, 4)),
                  index=['产品A', '产品B', '产品C', '产品D', '产品E'],
                  columns=['Q1', 'Q2', 'Q3', 'Q4'])

heatmap_data = []
for i, row_label in enumerate(df.index):      # i 是纵轴索引
    for j, col_label in enumerate(df.columns): # j 是横轴索引
        value = int(df.iat[i, j])              # 获取单元格值并转成 Python 整数
        heatmap_data.append([j, i, value])

print(f"转换后的前5条数据: {heatmap_data[:5]}")
print(f"数据总数: {len(heatmap_data)}")
转换后的前5条数据: [[0, 0, 51], [1, 0, 92], [2, 0, 14], [3, 0, 71], [0, 1, 60]]
数据总数: 20

关键点:enumerate 用于获取横、纵轴索引,df.iat[i, j] 用于通过整数位置索引获取值。绘制时,将 df.columns 和 df.index 分别作为横、纵轴标签即可。

本章小结

本节我们学习了热力图这一重要的数据关系可视化图表。它擅长将矩阵数据中每个单元格的数值,通过颜色这一直观的视觉通道进行编码,帮助我们快速发现数据中的高点、低点以及分布模式。

📋 行动清单

  • 掌握数据格式:能够将二维表格数据(如DataFrame)转换为热力图所需的 [x索引, y索引, 数值] 列表格式。
  • 理解视觉映射:学会配置 VisualMapOpts,理解 min_、max_ 和 range_color 参数如何控制数值到颜色的映射关系。
  • 应用图表解读:面对一个热力图,能够从颜色分布、行列对比中描述出数据的大致规律和特征点。

—— 小象教研组