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

3D柱状图与折线图

3D柱状图、折线图与散点图

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

在掌握了二维图表后,我们如何直观地展示包含三个维度的数据关系?本节将带你进入三维可视化的世界,学习使用 pyecharts 绘制 3D 柱状图、3D 折线图和 3D 散点图。你将学会如何将二维平面上的数据点,通过高度(Z轴)或空间连线,转化为更具空间感和层次感的立体图表,从而更全面地观察和分析多维度数据。

💡 核心导读

  • 三维图表的核心:理解 X、Y、Z 三个坐标轴在图表中的角色,通常 X、Y 轴表示分类或连续变量,Z 轴表示数值大小。
  • 数据格式转换:掌握为 3D 图表准备数据的方法,特别是如何将原始数据列表转换为 [[x, y, z], ...] 的格式。
  • 图表间的联系:认识到 3D 柱状图与热力图在视觉上的内在联系,理解从不同视角观察同一组数据可以揭示不同的信息。
  • 交互与配置:学会配置 3D 图表的坐标轴、视觉映射(visualMap),并利用其内置的旋转、缩放、高亮等交互功能进行探索。

1. 理解三维图表:从热力图到立体空间

在二维图表中,我们常用颜色深浅(如热力图)来表示第三个维度的数值大小。三维图表则将这个“数值”维度具象化为空间中的高度(Z轴),用柱子的高低或点在空间中的位置来呈现。

想象一个场景:我们想观察一周(7天)内,每小时(24小时)的网站访问量。这是一个典型的“天 × 时 × 访问量”的三维数据。

  • 热力图:用二维网格表示“天”和“时”,每个格子的颜色深浅代表“访问量”。
  • 3D柱状图:在二维网格的基础上,为每个格子“生长”出一个高度与“访问量”成正比的柱子。

3D图表提供了更丰富的视觉编码(高度+颜色),但同时也可能因柱子遮挡而影响观察。因此,它更适合数据量适中、需要强调空间分布和对比的场景。

2. 准备三维数据

三维图表的数据通常是一个列表,列表中的每个元素是一个包含三个值的子列表,分别对应 X、Y、Z 坐标。

我们以“一周每小时访问量”为例,生成模拟数据。注意,为了确保每次运行结果一致,我们固定随机数种子。

import random
from pyecharts import options as opts
from pyecharts.charts import Bar3D

random.seed(2024)

bar3d_data = [(i, j, random.randint(0, 50)) for i in range(7) for j in range(24)]
formatted_data = [[d[1], d[0], d[2]] for d in bar3d_data]
xaxis_labels = [str(i) for i in range(24)]
yaxis_labels = ["Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri"]

bar3d_chart = (
    Bar3D()
    .add(
        series_name="访问量",
        data=formatted_data,
        xaxis3d_opts=opts.Axis3DOpts(data=xaxis_labels, type_="category", name="小时"),
        yaxis3d_opts=opts.Axis3DOpts(data=yaxis_labels, type_="category", name="星期"),
        zaxis3d_opts=opts.Axis3DOpts(type_="value", name="访问量"),
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title="一周每小时网站访问量 (3D柱状图)"),
        visualmap_opts=opts.VisualMapOpts(
            max_=50,
            range_color=["#313695", "#4575b4", "#74add1", "#abd9e9", "#e0f3f8", "#ffffbf", "#fee090", "#fdae61", "#f46d43", "#d73027", "#a50026"],
        ),
    )
)

bar3d_chart.render_embed()
print("原始数据格式示例(前5条):")
for i in range(5):
    print(f"  星期{bar3d_data[i][0]}, 小时{bar3d_data[i][1]:02d}, 访问量{bar3d_data[i][2]}")
print(f"数据总条数: {len(bar3d_data)}")
print(f"图表类型: {type(bar3d_chart).__name__}")
print(f"X轴标签: {xaxis_labels[:3]} ... {xaxis_labels[-3:]}")
print(f"Y轴标签: {yaxis_labels}")
3D 柱状图,底面为星期与小时,柱高表示访问量
图:3D 柱状图:星期与小时构成底面网格,柱高表示访问量,颜色深浅辅助区分高度。手机端可左右滑动查看,点击图片可看全图。
原始数据格式示例(前5条):
  星期0, 小时00, 访问量30
  星期0, 小时01, 访问量11
  星期0, 小时02, 访问量46
  星期0, 小时03, 访问量37
  星期0, 小时04, 访问量19
数据总条数: 168
图表类型: Bar3D
X轴标签: ['0', '1', '2'] ... ['21', '22', '23']
Y轴标签: ['Sat', 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri']

我们的数据 (星期几, 小时, 访问量) 被映射到图表的 (X轴, Y轴, Z轴):小时作为 X 轴分类,星期几作为 Y 轴分类,访问量作为 Z 轴数值。上面的完整块把数据生成、格式转换和图表构建放在一起,单独运行即可复现。

3. 绘制3D柱状图 (Bar3D)

Bar3D 的关键是先把数据转换为 [[x, y, z], ...],再配置三个坐标轴和视觉映射。

代码解读与交互:

  1. 数据转换:[[d[1], d[0], d[2]] for d in bar3d_data] 是关键,它将原始数据 (i, j, value) 转换为了图表所需的 (x, y, z) 格式,即 (小时, 星期几, 访问量)。
  2. 坐标轴配置:Axis3DOpts 用于配置3D坐标轴。type_="category" 表示分类轴,需要提供 data 标签;type_="value" 表示数值轴。
  3. 视觉映射 (visualMap):VisualMapOpts 将 Z 轴的数值映射到颜色。max_=50 设定了颜色映射的范围上限,颜色从低值(冷色)渐变到高值(暖色)。
  4. 交互功能:生成的 HTML 图表支持鼠标拖拽旋转视角、滚轮缩放。将鼠标悬停在任意柱子上,会显示该点的具体坐标和数值。

与热力图的关系:尝试在生成的 HTML 图表中,用鼠标拖动,将视角调整到正上方(俯视)。你会发现,柱子顶面的颜色分布,非常类似于一个用相同颜色映射的热力图。3D柱状图可以看作是热力图在三维空间中的立体延伸。

4. 绘制3D折线图与散点图

使用同一组数据,只需更换图表类,并做微调,即可得到3D折线图和散点图。

4.1 3D折线图 (Line3D)

3D折线图将三维空间中的点按顺序连接起来,适合展示在三维空间中数据的变化趋势或路径。

import random
from pyecharts import options as opts
from pyecharts.charts import Line3D

random.seed(2024)
bar3d_data = [(i, j, random.randint(0, 50)) for i in range(7) for j in range(24)]
formatted_data = [[d[1], d[0], d[2]] for d in bar3d_data]
xaxis_labels = [str(i) for i in range(24)]
yaxis_labels = ["Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri"]

line3d_chart = (
    Line3D()
    .add(
        series_name="访问量趋势",
        data=formatted_data,  # 使用与柱状图相同格式的数据
        xaxis3d_opts=opts.Axis3DOpts(data=xaxis_labels, type_="category", name="小时"),
        yaxis3d_opts=opts.Axis3DOpts(data=yaxis_labels, type_="category", name="星期"),
        zaxis3d_opts=opts.Axis3DOpts(type_="value", name="访问量"),
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title="一周每小时网站访问量 (3D折线图)"),
        visualmap_opts=opts.VisualMapOpts(max_=50)
    )
)

line3d_chart.render_embed()
print(f"图表类型: {type(line3d_chart).__name__}")
print(f"数据点数量: {len(formatted_data)}")
图表类型: Line3D
数据点数量: 168

4.2 3D散点图 (Scatter3D)

3D散点图只绘制三维空间中的点,不进行连接,适合展示数据的分布情况,尤其是当数据点没有严格顺序或需要观察聚类时。

import random
from pyecharts import options as opts
from pyecharts.charts import Scatter3D

random.seed(2024)
bar3d_data = [(i, j, random.randint(0, 50)) for i in range(7) for j in range(24)]
formatted_data = [[d[1], d[0], d[2]] for d in bar3d_data]
xaxis_labels = [str(i) for i in range(24)]
yaxis_labels = ["Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri"]

scatter3d_chart = (
    Scatter3D()
    .add(
        series_name="访问量分布",
        data=formatted_data,
        xaxis3d_opts=opts.Axis3DOpts(data=xaxis_labels, type_="category", name="小时"),
        yaxis3d_opts=opts.Axis3DOpts(data=yaxis_labels, type_="category", name="星期"),
        zaxis3d_opts=opts.Axis3DOpts(type_="value", name="访问量"),
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title="一周每小时网站访问量 (3D散点图)"),
        visualmap_opts=opts.VisualMapOpts(max_=50)
    )
)

scatter3d_chart.render_embed()
print(f"图表类型: {type(scatter3d_chart).__name__}")
print(f"数据点数量: {len(formatted_data)}")
3D 散点图,数据点按星期与小时分布、颜色表示访问量
图:3D 散点图:同一份访问量数据换成散点后,点的高低与颜色同时表达数值大小。手机端可左右滑动查看,点击图片可看全图。
图表类型: Scatter3D
数据点数量: 168

对比与选择:

  • 柱状图:强调每个独立分类(每个小时×每天)的绝对数值大小和对比。
  • 折线图:强调同一分类(同一天)下,随着另一个维度(小时)变化的趋势。
  • 散点图:强调数据点在三维空间中的整体分布和密度,避免线条可能带来的误导性趋势暗示。

在实际使用中,如果数据点过多,3D散点图可能会显得拥挤,而3D柱状图则可能产生严重遮挡。这时需要谨慎选择,或通过设置透明度、采样等方法来优化可视化效果。

📝 动手练一练

  1. 数据轴与分类轴:在本节的3D柱状图示例中,把 X 轴的 type_ 改为 "value" 并移除 data=xaxis_labels,观察图表变化。

  2. 自定义视觉映射:把 3D 散点图的 range_color 设为 ["#0000FF", "#FF0000"],比较低值蓝色、高值红色的映射效果。

点击查看合并参考答案
import random
from pyecharts import options as opts
from pyecharts.charts import Bar3D
from pyecharts.charts import Scatter3D

random.seed(2024)
bar3d_data = [(i, j, random.randint(0, 50)) for i in range(7) for j in range(24)]
formatted_data = [[d[1], d[0], d[2]] for d in bar3d_data]
xaxis_labels = [str(i) for i in range(24)]
yaxis_labels = ["Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri"]

value_axis_chart = (
    Bar3D()
    .add(
        series_name="访问量",
        data=formatted_data,
        xaxis3d_opts=opts.Axis3DOpts(type_="value", name="小时"),
        yaxis3d_opts=opts.Axis3DOpts(data=yaxis_labels, type_="category", name="星期"),
        zaxis3d_opts=opts.Axis3DOpts(type_="value", name="访问量"),
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title="X轴为数值轴的3D柱状图"),
        visualmap_opts=opts.VisualMapOpts(max_=50)
    )
)

custom_color_chart = (
    Scatter3D()
    .add(
        series_name="访问量分布",
        data=formatted_data,
        xaxis3d_opts=opts.Axis3DOpts(data=xaxis_labels, type_="category", name="小时"),
        yaxis3d_opts=opts.Axis3DOpts(data=yaxis_labels, type_="category", name="星期"),
        zaxis3d_opts=opts.Axis3DOpts(type_="value", name="访问量"),
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title="双色视觉映射的3D散点图"),
        visualmap_opts=opts.VisualMapOpts(
            max_=50,
            range_color=["#0000FF", "#FF0000"]  # 从蓝到红
        )
    )
)

value_axis_chart.render_embed()
custom_color_chart.render_embed()
print("数值轴图表:", type(value_axis_chart).__name__)
print("双色散点图:", type(custom_color_chart).__name__)
数值轴图表: Bar3D
双色散点图: Scatter3D

本章小结

本节我们探索了 pyecharts 中的三维基础图表:柱状图、折线图和散点图。通过“一周每小时访问量”的案例,你学会了如何为3D图表准备和转换数据格式,如何配置三维坐标轴,以及如何利用视觉映射和交互功能来探索数据。

📋 行动清单

  • 动手实践:运行本节三个核心示例,将 render_embed() 返回的 HTML 嵌入 Notebook 或网页,并体验旋转、缩放、悬停提示等交互。
  • 思考联系:尝试将3D柱状图的视角调整到正上方,理解其与二维热力图在视觉表达上的异同。
  • 应用拓展:设想一个你自己的三维数据集(例如,不同城市、不同月份的平均温度),模仿本节流程,尝试用合适的3D图表进行可视化。

—— 小象教研组