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

其他统计图表

分组箱形图与叠加图:统计图表的进阶应用

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

在掌握了基础的箱形图、直方图以及3D图表后,我们继续探索统计图表中更实用的两种高级形式:分组箱形图和叠加图。这两种图表能帮助我们进行更精细的数据对比和复合信息展示,是数据分析报告中常见的利器。本节将带你理解它们的适用场景,并掌握在pyecharts中的实现方法。

💡 核心导读

  • 分组箱形图:用于比较多个分组内不同类别的数据分布,例如比较A、B两组在上午和下午的业绩分布差异。
  • 叠加图:将两个或多个共享X轴但Y轴刻度不同的图表叠加在一起,常用于展示关联但量纲不同的指标,如降水量与温度随月份的变化。
  • 数据准备是关键:分组箱形图的数据需要按特定顺序(先组内类别,再跨组)组织;叠加图则需要为每个系列指定正确的Y轴索引。
  • 坐标轴管理:叠加图的核心在于使用extend_axis扩展坐标轴,并通过yaxis_index参数为每个数据系列绑定正确的Y轴。

一、分组箱形图:多维度分布对比

分组箱形图是基础箱形图的扩展,它允许我们在一个图表中同时展示多个分组(如A组、B组),并且每个分组内还可以细分多个类别(如上午、下午)。这种图表非常适合用于对比不同群体在不同条件下的数据分布情况,例如不同部门在不同季度的销售额分布、不同实验组在不同时间点的测量值分布等。

1. 数据准备:理解结构

绘制分组箱形图前,数据组织是第一步,也是最关键的一步。数据需要按照“先类别,后分组”的嵌套结构来准备。

假设我们要比较A组和B组在“上午”和“下午”两个时间段的数据分布。我们需要准备两个列表:v1代表上午数据,v2代表下午数据。而每个列表内部,又包含两个子列表,分别对应A组和B组在该时间段的具体数值。

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

# 上午数据:先组内类别(A/B),再跨时间段
v1 = [
    [850, 740, 900, 1070, 930, 850, 950, 980, 980, 880, 1000, 980],  # A组上午数据
    [960, 940, 960, 940, 880, 800, 850, 880, 900, 840, 830, 790],    # B组上午数据
]

v2 = [
    [890, 810, 810, 820, 800, 770, 760, 740, 750, 760, 910, 920],    # A组下午数据
    [890, 840, 780, 810, 760, 810, 790, 810, 820, 850, 870, 870],    # B组下午数据
]
boxplot = (
    Boxplot()
    .add_xaxis(["A组", "B组"])
    .add_yaxis("上午", Boxplot().prepare_data(v1))
    .add_yaxis("下午", Boxplot().prepare_data(v2))
    .set_global_opts(
        title_opts=opts.TitleOpts(title="分组箱形图示例:A/B组上下午数据分布"),
        yaxis_opts=opts.AxisOpts(name="数值"),
        xaxis_opts=opts.AxisOpts(name="分组"),
    )
)

boxplot.render_embed()
print(f"图表类型: {type(boxplot).__name__}")
print("X轴分类: ['A组', 'B组']")
print("Y轴系列名称: ['上午', '下午']")
A组与B组上下午数据的分组箱形图
图:分组箱形图:A、B 两组的上下午数据并排显示,可以直接比较中位数位置和离散程度。手机端可左右滑动查看,点击图片可看全图。

输出摘要:

图表类型: Boxplot
X轴分类: ['A组', 'B组']
Y轴系列名称: ['上午', '下午']

数据格式说明:

  • v1[0] / v1[1]:A组、B组的上午数据。
  • v2[0] / v2[1]:A组、B组的下午数据。

prepare_data() 返回的仍是嵌套列表,但每组原始值已被替换为“最小值、Q1、中位数、Q3、最大值”五数。上面的完整块把数据准备、统计值计算和绘图串在一起,单独运行即可复现。

2. 使用 prepare_data 计算统计值

箱形图展示的不是原始数据点,而是五个关键统计值。Boxplot.prepare_data() 会自动完成这项计算,调用结果随后直接交给 .add_yaxis()。

3. 绘制分组箱形图

X 轴设置分组标签“A组、B组”,两个 Y 轴系列分别表示“上午”和“下午”。你可以通过箱子位置、长度和异常点比较组间与组内差异。 运行上述代码后,你将得到一个清晰的分组箱形图。在“A组”和“B组”下方,会并列显示两个箱子,分别代表该组的“上午”和“下午”数据。通过观察箱子的位置、长度和异常点,可以直观对比:

  • 组间差异:A组和B组的整体水平高低。
  • 组内差异:在同一个组内,上午和下午的数据分布有何不同(如中位数偏移、离散程度变化)。

二、叠加图:多指标复合分析

叠加图,或称双Y轴图,用于在共享同一个X轴的前提下,展示两个量纲(单位)不同但存在关联的数据系列。最常见的例子就是将柱状图(如降水量,单位毫升)和折线图(如平均温度,单位摄氏度)叠加在一起,分析它们随时间(月份)的协同变化趋势。

1. 核心概念:扩展坐标轴与轴索引

叠加图的核心技术点在于管理多个Y轴。在pyecharts中,我们通过以下步骤实现:

  1. 创建主图表并扩展Y轴:通常以第一个图表(如柱状图)为主图表,使用extend_axis方法为其添加一个新的、独立的Y轴。
  2. 指定系列使用的Y轴:第二个图表(如折线图)在添加数据时,需要通过yaxis_index参数明确指定它使用我们刚刚扩展出来的第二个Y轴(索引为1,因为第一个Y轴索引为0)。

2. 数据准备与图表绘制

我们以“某地月度降水量与平均温度”为例。

# 叠加图数据准备
precipitation = [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3]  # 降水量 (ml)
temperature = [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2]       # 平均温度 (°C)

from pyecharts.charts import Bar, Line
from pyecharts import options as opts
from pyecharts.faker import Faker

# 1. 创建主图表(柱状图 - 降水量)
bar = (
    Bar()
    .add_xaxis(Faker.months)  # X轴:月份
    .add_yaxis("降水量", precipitation)
    # 关键步骤:扩展第二个Y轴,用于温度数据
    .extend_axis(
        yaxis=opts.AxisOpts(
            name="温度",
            axislabel_opts=opts.LabelOpts(formatter="{value} °C"),  # 设置轴标签格式
            interval=5,  # 刻度间隔
            position="right",  # 将第二个Y轴放在右侧
        )
    )
    # 隐藏柱状图上的数据标签,避免图表过于拥挤
    .set_series_opts(label_opts=opts.LabelOpts(is_show=False))
    .set_global_opts(
        title_opts=opts.TitleOpts(title="月度降水量与温度叠加图"),
        # 设置主Y轴(左侧)的标签格式
        yaxis_opts=opts.AxisOpts(
            name="降水量",
            axislabel_opts=opts.LabelOpts(formatter="{value} ml")
        ),
    )
)

# 2. 创建第二个图表(折线图 - 温度)
line = (
    Line()
    .add_xaxis(Faker.months)
    .add_yaxis(
        "平均温度",
        temperature,
        yaxis_index=1,  # 关键步骤:指定使用索引为1的Y轴(即我们扩展的那个)
        label_opts=opts.LabelOpts(is_show=True),  # 显示折线点的数值标签
    )
)

# 3. 将折线图叠加到柱状图上
combined_chart = bar.overlap(line)

# 输出配置摘要
print(f"主图表类型: {type(bar).__name__}")
print(f"叠加图表类型: {type(line).__name__}")
print(f"最终组合图表类型: {type(combined_chart).__name__}")
print("Y轴数量: 2")
print("数据系列数量及名称: ['降水量', '平均温度']")
月度降水量柱形与温度折线叠加的双轴组合图
图:柱线叠加图:柱形表示月度降水量,折线表示温度,两条数值轴分别对应各自的量纲。手机端可左右滑动查看,点击图片可看全图。

输出摘要:

主图表类型: Bar
叠加图表类型: Line
最终组合图表类型: Bar
Y轴数量: 2
数据系列数量及名称: ['降水量', '平均温度']

3. 关键参数解析与常见问题

  • yaxis_index=1:这是最容易出错的地方。如果不设置或设置为0,折线图将使用左侧的降水量Y轴,导致温度数据(数值在0-25之间)在0-200的刻度上几乎变成一条平坦的直线,完全失真。务必确保第二个系列绑定到正确的Y轴。
  • 坐标轴位置:通过position="right"可以将扩展的Y轴放置在右侧,使图表更符合阅读习惯。
  • overlap方法:在 pyecharts 2.1.0 中,bar.overlap(line) 会返回以 Bar 为主体的组合图表对象,并把折线系列叠加进来;所以最终打印的图表类型仍是 Bar。

错误示例:如果忘记在折线图中设置yaxis_index=1,温度数据将错误地映射到降水量的坐标轴上。

# 错误示例:折线图未指定yaxis_index
line_wrong = Line().add_xaxis(Faker.months).add_yaxis("平均温度", temperature)
combined_wrong = bar.overlap(line_wrong)
# 此时温度折线将几乎紧贴X轴,无法观察其变化趋势。

📝 动手练一练

  1. 改造分组箱形图:假设现在有第三组数据“C组”,其上午和下午的数据如下。请修改代码,将C组也加入到分组箱形图中进行对比。
    • C组上午: [920, 880, 870, 850, 890, 910, 930, 900, 880, 860, 940, 920]
    • C组下午: [830, 820, 810, 800, 850, 840, 830, 820, 810, 860, 850, 840]
点击查看参考答案
from pyecharts.charts import Boxplot
from pyecharts import options as opts

# 扩展数据,加入C组
v1 = [
    [850, 740, 900, 1070, 930, 850, 950, 980, 980, 880, 1000, 980],  # A组上午
    [960, 940, 960, 940, 880, 800, 850, 880, 900, 840, 830, 790],    # B组上午
    [920, 880, 870, 850, 890, 910, 930, 900, 880, 860, 940, 920],    # C组上午(新增)
]

v2 = [
    [890, 810, 810, 820, 800, 770, 760, 740, 750, 760, 910, 920],    # A组下午
    [890, 840, 780, 810, 760, 810, 790, 810, 820, 850, 870, 870],    # B组下午
    [830, 820, 810, 800, 850, 840, 830, 820, 810, 860, 850, 840],    # C组下午(新增)
]

boxplot = (
    Boxplot()
    .add_xaxis(["A组", "B组", "C组"])  # X轴增加C组
    .add_yaxis("上午", Boxplot().prepare_data(v1))
    .add_yaxis("下午", Boxplot().prepare_data(v2))
    .set_global_opts(title_opts=opts.TitleOpts(title="三组数据上下午分布对比"))
)

print("X轴分组: ['A组', 'B组', 'C组']")

输出摘要:

X轴分组: ['A组', 'B组', 'C组']
  1. 创建销售额与增长率叠加图:现有某产品月度销售额(单位:万元)和环比增长率(单位:%)数据。请绘制一个叠加图,用柱状图表示销售额(左侧Y轴),用折线图表示增长率(右侧Y轴)。
    • 销售额: [120, 135, 150, 110, 160, 185, 200, 220, 210, 230, 250, 280]
    • 增长率: [None, 12.5, 11.1, -26.7, 45.5, 15.6, 8.1, 10.0, -4.5, 9.5, 8.7, 12.0] (注意:第一个月无增长率,用None表示)
点击查看参考答案
from pyecharts.charts import Bar, Line
from pyecharts import options as opts
from pyecharts.faker import Faker

sales = [120, 135, 150, 110, 160, 185, 200, 220, 210, 230, 250, 280]
growth_rate = [None, 12.5, 11.1, -26.7, 45.5, 15.6, 8.1, 10.0, -4.5, 9.5, 8.7, 12.0]

bar = (
    Bar()
    .add_xaxis(Faker.months)
    .add_yaxis("销售额(万元)", sales)
    .extend_axis(
        yaxis=opts.AxisOpts(
            name="增长率",
            axislabel_opts=opts.LabelOpts(formatter="{value}%"),
            position="right"
        )
    )
    .set_series_opts(label_opts=opts.LabelOpts(is_show=False))
    .set_global_opts(
        title_opts=opts.TitleOpts(title="月度销售额与增长率"),
        yaxis_opts=opts.AxisOpts(name="销售额(万元)")
    )
)

line = (
    Line()
    .add_xaxis(Faker.months)
    .add_yaxis(
        "环比增长率",
        growth_rate,
        yaxis_index=1,
        label_opts=opts.LabelOpts(formatter="{c}%", is_show=True)
    )
)

combined = bar.overlap(line)
print("图表标题: 月度销售额与增长率")
print("左侧Y轴名称: 销售额(万元)")
print("右侧Y轴名称: 增长率")

输出摘要:

图表标题: 月度销售额与增长率
左侧Y轴名称: 销售额(万元)
右侧Y轴名称: 增长率

本章小结

在本节中,我们深入学习了两种高级统计图表:分组箱形图和叠加图。分组箱形图通过嵌套的数据结构,实现了多分组、多类别的分布对比,是进行精细化差异分析的强大工具。叠加图则通过扩展和管理多个Y轴,将不同量纲的数据融合在一张图中,清晰展现其关联关系与变化趋势,极大地提升了信息表达的效率。

📋 行动清单

  • 练习数据组织:尝试用Python列表生成模拟数据,并按照分组箱形图要求的“先类别,后分组”结构进行整理。
  • 掌握轴索引:在PyCharm或Jupyter中,亲手敲一遍叠加图的代码,故意修改或删除yaxis_index=1参数,观察图表发生的变化,加深理解。
  • 思考应用场景:回顾你最近所做的数据分析项目,思考哪些场景适合使用分组箱形图或叠加图来呈现结果,并尝试用本节所学进行重构。

—— 小象教研组