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

其他成分比例图表

多饼图、玫瑰图与雷达图:成分比例的高级可视化

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

在掌握了饼图、圆环图与矩形树图这些基础的成分比例图表后,我们常常会遇到更复杂的分析场景:需要同时比较多个类别的占比分布,或者需要在一个图表中展现多个维度的数据。本节将介绍三种功能强大且视觉表现力丰富的成分比例图表——多饼图、玫瑰图和雷达图。通过学习它们,你将能够应对更复杂的业务分析需求,例如多维度对比、数据排名以及多维属性评估。

💡 核心导读

  • 多饼图:通过在同一画布上放置多个饼图或圆环图,直观对比不同类别下相同数据项的占比情况,尤其适合“某特定项 vs 其他项”的横向比较。
  • 玫瑰图:一种特殊的饼图变体,它利用扇形的半径长短来编码数据大小,可以更清晰地展示数据间的量级差异,常用于数据排名展示。
  • 雷达图:用于展示多维数据(通常四维及以上)的图表,每个维度对应一个坐标轴。它能清晰呈现个体在多个维度上的综合表现,常用于能力评估或对比分析。
  • 统一度量:在绘制雷达图前,必须通过 schema 配置将所有维度的数据统一到相同的度量尺度上,这是雷达图正确绘制的关键前提。
  • 灵活配置:无论是多饼图的布局 (center, radius),还是玫瑰图的模式 (rosetype),都体现了 ECharts 通过配置项实现丰富图表表现力的核心思想。

一、多饼图:并排比较的艺术

多饼图并非一个独立的图表类型,而是在同一画布上通过不同的 center 和 radius 定位多个饼图(或圆环图)来实现的。它不依赖直角坐标系的 Grid 组件。它的核心应用场景是横向比较。例如,比较不同电影题材(如剧情、奇幻、爱情、惊悚)中,该题材票房占总票房的比例,同时将“其他”题材作为统一的对比基准。

数据准备与基本绘制

多饼图的绘制本质上是多次调用 Pie().add() 方法,每次指定不同的数据、位置和大小。

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

# 示例数据:四个电影题材及其"票房"占比,均包含"其他"项用于对比
data_sets = [
    (["剧情", "其他"], [25, 75]),
    (["奇幻", "其他"], [24, 76]),
    (["爱情", "其他"], [14, 86]),
    (["惊悚", "其他"], [11, 89]),
]

# 初始化一个饼图画布
multi_pie = Pie()

# 循环添加四个圆环图到画布的不同位置
centers = [["20%", "30%"], ["55%", "30%"], ["20%", "70%"], ["55%", "70%"]] # 定义四个圆环的中心位置
for i, (categories, values) in enumerate(data_sets):
    # 将数据转换为pyecharts需要的列表格式:[ [name1, value1], [name2, value2] ... ]
    pie_data = [list(z) for z in zip(categories, values)]
    
    multi_pie.add(
        series_name="",  # 系列名留空,因为图例我们统一管理
        data_pair=pie_data,
        center=centers[i],  # 设置当前圆环图的位置
        radius=[60, 80],    # 内半径60,外半径80(单位:像素),形成圆环
    )

# 设置全局配置:标题和图例
multi_pie.set_global_opts(
    title_opts=opts.TitleOpts(title="多饼图示例:不同电影题材票房占比"),
    legend_opts=opts.LegendOpts(
        pos_top="20%",      # 图例顶部距离容器顶部20%
        pos_left="80%",     # 图例左侧距离容器左侧80%
        orient="vertical",  # 图例垂直排列
    ),
)

# 设置系列配置:格式化标签显示为"名称: 百分比%"
multi_pie.set_series_opts(
    label_opts=opts.LabelOpts(formatter="{b}: {d}%")
)

# 生成图表并打印稳定的输入摘要(不读取 options 中的嵌套对象)
multi_pie.render_embed()
print(f"图表类型: {type(multi_pie).__name__}")
print(f"包含的系列数量: {len(data_sets)}")
for idx, ((categories, _), center) in enumerate(zip(data_sets, centers)):
    print(f"  系列{idx} 中心位置: {center}, 数据项: {categories}")
四个电影题材票房占比饼图并排展示
图:多饼图:四个电影题材的票房占比并排对比,共享同一组颜色图例。手机端可左右滑动查看,点击图片可看全图。

输出示例(具体数值取决于运行环境):

图表类型: Pie
包含的系列数量: 4
  系列0 中心位置: ['20%', '30%'], 数据项: ['剧情', '其他']
  系列1 中心位置: ['55%', '30%'], 数据项: ['奇幻', '其他']
  系列2 中心位置: ['20%', '70%'], 数据项: ['爱情', '其他']
  系列3 中心位置: ['55%', '70%'], 数据项: ['惊悚', '其他']

关键配置解析:

  • center: 定义饼图中心在容器中的位置,格式为 [水平位置, 垂直位置],通常使用百分比(如 "20%"),代表相对于容器宽度/高度的比例。
  • radius: 定义饼图的半径。传入单个数字(如 80)表示实心饼图半径;传入列表 [内半径, 外半径](如 [60, 80])则绘制圆环图。值可以是像素数字,也可以是百分比字符串。
  • 图例管理:当多个饼图共享相同的数据项名称(如“其他”)时,ECharts 会自动合并图例。点击图例可以交互式地显示或隐藏所有饼图中对应的数据项,方便对比。

灵活性与应用思考

多饼图的强大之处在于其灵活性。你可以轻松调整每个饼图的位置、大小,甚至使用不同的数据项。例如,你可以让其中两个饼图比较“A产品 vs 其他”,另外两个比较“B产品 vs 其他”,从而实现更复杂的业务对比。

二、玫瑰图:用半径讲故事

玫瑰图(南丁格尔玫瑰图)是饼图的一种变形。在普通饼图中,数据大小由扇形的弧长(圆心角)决定。而在玫瑰图中,数据大小主要由扇形的半径长度来表现。这使得数据间的量级差异在视觉上被放大,更容易识别排名和差距。

rosetype 的两种模式

玫瑰图通过饼图系列配置项 rosetype 来开启,它有两种模式:

  1. radius 模式:扇形的圆心角表示该数据在总和中的百分比,扇形的半径表示数据的实际数值大小。即同时用角度和半径编码信息。
  2. area 模式:所有扇形的圆心角相同(均匀分布),仅通过扇形的半径长度来表示数据的实际数值大小。
from pyecharts import options as opts
from pyecharts.charts import Pie

# 准备数据:假设为某季度各品牌手机销量
brands = ["Apple", "Xiaomi", "Huawei", "Oppo", "Vivo", "Others"]
sales = [146, 140, 98, 29, 27, 45]

pie_data_rose = [list(z) for z in zip(brands, sales)]

# 创建玫瑰图 - radius模式
rose_chart_radius = (
    Pie()
    .add(
        series_name="手机销量",
        data_pair=pie_data_rose,
        radius=["15%", "70%"],  # 内半径15%,外半径70%,留出中心空间
        rosetype="radius",       # 设置为radius模式
        label_opts=opts.LabelOpts(formatter="{b}: {c}台"), # 标签显示实际数值
    )
    .set_global_opts(title_opts=opts.TitleOpts(title="玫瑰图 (rosetype='radius')"))
)

# 创建玫瑰图 - area模式
rose_chart_area = (
    Pie()
    .add(
        series_name="手机销量",
        data_pair=pie_data_rose,
        radius=["15%", "70%"],
        rosetype="area",         # 设置为area模式
        label_opts=opts.LabelOpts(formatter="{b}: {c}台"),
    )
    .set_global_opts(title_opts=opts.TitleOpts(title="玫瑰图 (rosetype='area')"))
)

# 打印摘要信息
print("=== radius模式玫瑰图 ===")
print(f"数据项数量: {len(pie_data_rose)}")
print(f"数据范围: {min(sales)} 到 {max(sales)}")
print("...(配置摘要已生成)")

print("\n=== area模式玫瑰图 ===")
print(f"数据项数量: {len(pie_data_rose)}")
print("提示:在此模式下,所有扇形角度相同,仅半径表示数据大小。")

输出示例:

=== radius模式玫瑰图 ===
数据项数量: 6
数据范围: 27 到 146
...(配置摘要已生成)

=== area模式玫瑰图 ===
数据项数量: 6
提示:在此模式下,所有扇形角度相同,仅半径表示数据大小。

如何选择模式?

  • 选择 radius:当你既关心每个部分占总体的比例(看角度),又关心它们之间的绝对数值差异(看半径)时。它信息量更丰富,但解读稍复杂。
  • 选择 area:当你只关注不同类别之间绝对数值的排名和对比,且希望避免因比例不同造成的角度误导时。视觉对比更直接、强烈。

三、雷达图:多维能力的画像

雷达图(又称蜘蛛网图)适用于展示个体在多个维度上的表现。每个维度从中心点辐射出一条轴,所有轴的长度相同,并在末端连接起来形成一个多边形。多个个体的多边形叠加在一起,便于综合对比。

关键步骤:使用 schema 统一度量

雷达图各维度(如“销售能力”、“技术能力”、“沟通能力”)的单位和量纲可能完全不同。直接绘制会导致图形失真。因此,必须通过 schema(模式/指标)将所有维度归一化到统一的度量范围内(通常是0到某个最大值)。

from pyecharts import options as opts
from pyecharts.charts import Radar

# 1. 准备多维数据
# 假设评估两个项目在六个维度的得分
dimensions = ["销售", "管理", "信息技术", "客服", "研发", "市场"]
project_a = [4300, 10000, 28000, 35000, 50000, 19000]  # 项目A的原始数据
project_b = [5000, 14000, 28000, 31000, 42000, 21000]  # 项目B的原始数据

# 2. 定义schema:为每个维度指定一个统一的评估最大值
# 这个最大值应略高于所有数据在该维度上的最大值,以留出视觉空间。
schema = [
    opts.RadarIndicatorItem(name="销售", max_=6500),   # 销售维度,统一到0-6500的尺度
    opts.RadarIndicatorItem(name="管理", max_=16000),  # 管理维度,统一到0-16000的尺度
    opts.RadarIndicatorItem(name="信息技术", max_=30000),
    opts.RadarIndicatorItem(name="客服", max_=38000),
    opts.RadarIndicatorItem(name="研发", max_=52000),
    opts.RadarIndicatorItem(name="市场", max_=25000),
]
schema_max = [6500, 16000, 30000, 38000, 52000, 25000]

# 3. 绘制雷达图
radar_chart = (
    Radar()
    .add_schema(schema=schema)  # 加载统一的度量schema
    .add(
        series_name="项目A",
        data=[project_a],  # 数据需要是双层列表格式
        linestyle_opts=opts.LineStyleOpts(color="#CD0000", width=2), # 设置线条样式
        areastyle_opts=opts.AreaStyleOpts(opacity=0.1), # 设置区域填充样式
    )
    .add(
        series_name="项目B",
        data=[project_b],
        linestyle_opts=opts.LineStyleOpts(color="#5CACEE", width=2),
        areastyle_opts=opts.AreaStyleOpts(opacity=0.1),
    )
    .set_series_opts(label_opts=opts.LabelOpts(is_show=False)) # 默认不显示数据点标签
    .set_global_opts(title_opts=opts.TitleOpts(title="项目能力评估雷达图"))
)

# 打印摘要
print(f"雷达图维度数量: {len(schema)}")
print("维度名称及统一最大值:")
for idx, (name, max_value) in enumerate(zip(dimensions, schema_max)):
    print(f"  {idx+1}. {name}: 0 ~ {max_value}")
print("已添加数据系列: ['项目A', '项目B']")

输出示例:

雷达图维度数量: 6
维度名称及统一最大值:
  1. 销售: 0 ~ 6500
  2. 管理: 0 ~ 16000
  3. 信息技术: 0 ~ 30000
  4. 客服: 0 ~ 38000
  5. 研发: 0 ~ 52000
  6. 市场: 0 ~ 25000
已添加数据系列: ['项目A', '项目B']

解读与交互: 生成的雷达图中,每个项目的六个维度得分被映射到0-最大值的范围内,并连接成多边形。面积越大、形状越饱满,代表综合能力越强。鼠标悬停在多边形顶点或边上,可以查看该点在原始数据中的具体数值。这种图表非常适合用于人才评估、产品竞品分析、个人技能盘点等场景。

📝 动手练一练

  1. 改造多饼图:将本节第一个多饼图示例中的四个圆环图,改为上下两行、左右三列的六宫格布局,比较六个不同的电影题材(如新增“动作”、“喜剧”)。请计算并设置合适的 center 位置参数。

    点击查看布局思路提示

    假设容器宽度和高度均为100%。六宫格布局,可以想象将宽度和高度分别三等分。每个圆环的中心可以设置在每个格子的中心点附近。例如,第一行第一列的中心可以是 [“16.5%“, “16.5%“],第一行第二列是 [“50%“, “16.5%“],第一行第三列是 [“83.5%“, “16.5%“],第二行以此类推。radius 需要相应调小以适应更密集的布局。

  2. 分析玫瑰图:在玫瑰图的 area 模式下,如果数据 [100, 50, 25],请问代表数值100的扇形半径是代表数值25的扇形半径的几倍?这反映了 area 模式的什么特点?

    点击查看参考答案

    在 area 模式下,ECharts 将数据值线性映射到扇形外半径,而不是开平方。默认实心饼图(内半径为 0)下,数值 100 的外半径是数值 25 的 100 / 25 = 4 倍;如果设置了圆环内半径,比值会因这个固定偏移而小于 4,需要按实际 [内半径, 外半径] 计算。 由于等圆心角下扇形视觉面积与半径平方成正比,ECharts 的线性半径映射会让大数值在面积上被放大,而不是压平差异。这正是 area 玫瑰图适合强调排名、但不适合精确比较面积的原因。

本章小结

本节我们深入学习了三种用于成分比例分析的高级图表。多饼图擅长并排对比,玫瑰图通过半径强化排名差异,雷达图则是多维数据综合评估的利器。掌握它们,意味着你能为“比较”这个数据分析的核心动作,配备更精准、更直观的可视化工具。

📋 行动清单

  • 动手实现:在本地Python环境中,成功运行本节所有示例代码,并通过调整参数(如center、rosetype、schema中的max_值)观察图表变化。
  • 场景匹配:回顾你最近所做的数据分析,思考哪些场景适合用多饼图、玫瑰图或雷达图来呈现,并尝试用pyecharts绘制草图。
  • 配置查阅:前往pyecharts官方文档,查看Pie和Radar组件的详细配置项列表,了解本节未涉及的更多自定义选项(如背景色、动画效果等)。

—— 小象教研组