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

折线图 面积图

折线图与面积图

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

在数据分析的旅程中,我们常常需要观察数据如何随时间而变化。无论是追踪网站日活用户的增长,还是监控商品价格的波动,时间趋势的可视化都是洞察数据动态规律的关键。本节我们将学习时间趋势可视化的两大基础图表——折线图与面积图,掌握如何用 pyecharts 将你的 pandas 时间序列数据转化为清晰、交互式的 Web 图表。

💡 核心导读

  • 折线图:用于展示连续二维数据随时间变化的趋势,是时间序列分析最直观的图表。
  • 坐标轴陷阱:不当的 Y 轴起点会严重歪曲数据趋势,需遵循可视化伦理。
  • 数据缩放(DataZoom):当时间点过多时,使用此交互组件可聚焦观察特定时段。
  • 面积图:在折线基础上填充颜色,更强调“总量”或“体积”的趋势感,而非精确数值。
  • 从数据到图表:掌握将 pandas DataFrame 中的日期与数值列转换为 pyecharts 可绘制格式的标准流程。

一、折线图:描绘时间的轨迹

折线图(Line Chart)是展示数据随时间推移而变化的经典工具。它通过将一系列数据点按时间顺序连接起来,形成一条或多条折线,从而清晰地揭示数据的趋势、周期性或异常点。

1.1 适用场景与数据格式

折线图适用于连续的二维数据。通常,一个维度是时间(X轴),另一个维度是数值(Y轴)。例如:

  • 某城市每日的空气质量指数(AQI)变化。
  • 电商平台商品随月份变化的销售额。
  • 应用程序每周的新增用户数。

其核心是展示数据在时间维度上的连续性和顺序性。

1.2 基础绘制方法

在 pyecharts 中,我们使用 Line 类来创建折线图。其基本绘制流程与之前学习的柱状图类似,采用链式调用。

# 示例1:使用模拟数据绘制基础折线图
from pyecharts import options as opts
from pyecharts.charts import Line

# 使用固定数据,避免 Faker 的未固定随机输出
x_data = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
y_data_a = [120, 132, 128, 145, 152, 168, 175]
y_data_b = [98, 105, 112, 118, 125, 131, 140]

line_chart = (
    Line()
    .add_xaxis(xaxis_data=x_data)
    .add_yaxis(series_name="产品A销量", y_axis=y_data_a)
    .add_yaxis(series_name="产品B销量", y_axis=y_data_b)
    .set_global_opts(
        title_opts=opts.TitleOpts(title="基础折线图:产品月度销量趋势"),
        tooltip_opts=opts.TooltipOpts(trigger="axis") # 鼠标悬停时显示所有系列数据
    )
)

# 打印图表类型和基本信息作为验证
print(f"图表类型: {type(line_chart).__name__}")
print(f"X轴数据点数量: {len(x_data)}")
print("系列名称: ['产品A销量', '产品B销量']")
图表类型: Line
X轴数据点数量: 7
系列名称: ['产品A销量', '产品B销量']
产品A与产品B七天销量折线图,横轴为周一到周日
图:基础折线图:产品A与产品B七天销量走势,坐标轴触发的提示框会同时显示两个系列的数据。手机端自动使用简化视图,点击图片可看大图。

1.3 使用真实数据:从 pandas 到折线图

在实际项目中,数据通常存储在 pandas DataFrame 中。下面演示如何将一份包含日期和 AQI 值的数据可视化。

# 示例2:使用模拟的AQI数据(替代外部CSV文件)
import pandas as pd
import numpy as np

# 生成模拟的日期序列和AQI数据
np.random.seed(2025)
date_rng = pd.date_range(start='2023-01-01', end='2023-12-31', freq='D')
aqi_values = np.random.randint(30, 150, size=len(date_rng)) + 20 * np.sin(np.arange(len(date_rng)) * 2 * np.pi / 30)

aqi_df = pd.DataFrame({
    '日期': date_rng.strftime('%Y-%m-%d'),
    'AQI': aqi_values.astype(int)
})

print("数据预览(前5行):")
print(aqi_df.head())
print(f"\n数据形状: {aqi_df.shape}")
数据预览(前5行):
           日期  AQI
0  2023-01-01   92
1  2023-01-02  128
2  2023-01-03  120
3  2023-01-04  135
4  2023-01-05  120

数据形状: (365, 2)
# 将pandas数据转换为pyecharts可用的列表格式并绘制
from pyecharts.charts import Line

aqi_line = (
    Line()
    .add_xaxis(xaxis_data=aqi_df['日期'].tolist())
    .add_yaxis(
        series_name="AQI指数",
        y_axis=aqi_df['AQI'].tolist(),
        label_opts=opts.LabelOpts(is_show=False) # 数据点过多,默认不显示标签
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title="2023年某城市空气质量指数(AQI)趋势"),
        xaxis_opts=opts.AxisOpts(
            name="日期",
            axislabel_opts=opts.LabelOpts(rotate=45) # 日期标签旋转45度避免重叠
        ),
        yaxis_opts=opts.AxisOpts(name="AQI"),
        tooltip_opts=opts.TooltipOpts(trigger="axis"),
    )
)

print("图表已创建。X轴数据点:", len(aqi_df['日期'].tolist()))
图表已创建。X轴数据点: 365

当时间点过多(如365天)时,X轴标签会拥挤不堪。此时,数据区域缩放组件(DataZoom) 就派上了用场。

# 示例3:为折线图添加DataZoom交互
aqi_line_with_zoom = (
    Line()
    .add_xaxis(xaxis_data=aqi_df['日期'].tolist())
    .add_yaxis(series_name="AQI指数", y_axis=aqi_df['AQI'].tolist())
    .set_global_opts(
        title_opts=opts.TitleOpts(title="带数据缩放的AQI趋势图"),
        datazoom_opts=[
            opts.DataZoomOpts(), # 滑动条型数据区域缩放(默认)
            opts.DataZoomOpts(type_="inside"), # 内置型,可在坐标系内拖动缩放
        ],
        tooltip_opts=opts.TooltipOpts(trigger="axis"),
    )
)

print("已为图表添加滑动条式和内置式两种DataZoom组件。")
已为图表添加滑动条式和内置式两种DataZoom组件。

1.4 重要使用建议与陷阱

  1. 区分数据线与坐标轴线:确保折线与坐标轴有足够的视觉距离,可通过调整 yaxis_opts 中的 min 和 max 参数来设置合适的Y轴范围。
  2. 控制折线数量:建议在同一图表中不要绘制超过4条折线,以免造成视觉混乱。过多系列应考虑使用多子图(Grid)或选项卡(Tab)。
  3. 避免过度装饰:图表的核心是传达信息,而非装饰。慎用过于花哨的线条样式、阴影或动画。
  4. 切勿歪曲趋势:这是可视化伦理的关键。永远不要通过刻意改变Y轴的起点来放大或缩小数据变化的视觉表现。比较下面两个理论上基于同一数据集的图表:
# 示例4:演示Y轴起点对趋势感知的影响(概念说明)
# 假设有一组平缓上升的数据
data = list(range(101, 111))  # 101, 102, ..., 110

# 正确的Y轴:从0或接近数据最小值开始
print("**正确做法**:Y轴从0开始,真实反映平缓的增长趋势。")
print(f"数据范围: {min(data)} 到 {max(data)}")
print("视觉上趋势线平缓。\n")

# 错误的Y轴:从接近数据最小值的点开始
print("**错误做法**:Y轴从100开始,视觉上夸大了变化。")
print(f"数据范围(在图表中): {min(data)} 到 {max(data)}")
print("但Y轴显示范围被压缩为 100 到 110,导致微小的绝对变化(9)被放大为相对巨大的变化(约9%),误导观众。")
**正确做法**:Y轴从0开始,真实反映平缓的增长趋势。
数据范围: 101 到 110
视觉上趋势线平缓。

**错误做法**:Y轴从100开始,视觉上夸大了变化。
数据范围(在图表中): 101 到 110
但Y轴显示范围被压缩为 100 到 110,导致微小的绝对变化(9)被放大为相对巨大的变化(约9%),误导观众。

二、面积图:强调趋势的“体积感”

面积图(Area Chart)可视作折线图的“填充版”。它在折线与水平轴(通常是X轴)之间的区域填充颜色或纹理。这种填充强化了“总量”或“累积值”的视觉印象,更适合传达趋势的规模或体积,而非某个时间点的精确值。

2.1 从折线图到面积图

在 pyecharts 中,面积图并非一个独立的图表类,而是通过为 Line 图表的系列(series)添加 areastyle_opts 配置项来实现。

# 示例5:绘制基础面积图
from pyecharts.charts import Line
from pyecharts import options as opts

categories = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
market_share_a = [42, 48, 51, 55, 58, 62, 65]
market_share_b = [28, 31, 34, 36, 39, 43, 47]

area_chart = (
    Line()
    .add_xaxis(xaxis_data=categories)
    .add_yaxis(
        series_name="市场占有率A",
        y_axis=market_share_a,
        areastyle_opts=opts.AreaStyleOpts(opacity=0.5), # 关键配置:设置区域样式,透明度0.5
        label_opts=opts.LabelOpts(is_show=False)
    )
    .add_yaxis(
        series_name="市场占有率B",
        y_axis=market_share_b,
        areastyle_opts=opts.AreaStyleOpts(opacity=0.5),
        label_opts=opts.LabelOpts(is_show=False)
    )
    .set_global_opts(title_opts=opts.TitleOpts(title="基础面积图"))
)

print("面积图已创建,通过'areastyle_opts'实现区域填充。")
print("系列数量: 2")
面积图已创建,通过‘areastyle_opts’实现区域填充。
系列数量: 2
市场占有率A与市场占有率B的七天面积图,曲线下方填充半透明色块
图:面积图:两条市场占有率曲线下方填充半透明颜色,趋势的规模感比纯折线更强。手机端自动使用简化视图,点击图片可看大图。

AreaStyleOpts 提供了丰富的自定义选项:

  • opacity:填充透明度(0-1)。
  • color:指定固定填充颜色。
  • 还支持渐变填充和纹理图片填充(高级用法,可查阅官方文档)。

注意:ECharts 的 origin 是原生 areaStyle 能力,但锁定版 pyecharts 2.1.0 的 AreaStyleOpts 没有该关键字参数;如需使用,只能通过原生字典或 JsCode 透传。

2.2 面积图的使用建议

  1. 避免层叠遮挡:当绘制多个面积系列时,如果数据值范围相近,后面的系列会遮挡前面的。通常需要通过 stack 参数将其设置为堆叠面积图,或者确保数据不严重重叠。
  2. 适合2-3组数据:与折线图类似,建议最多展示不超过4组数据系列。过多的填充区域会使图表难以阅读。
  3. 慎用于数值相差过大的系列:如果两组数据的数值量级相差悬殊(例如一个在1000左右,一个在10左右),那么数值小的面积区域在图表中会几乎显示为一条线,失去面积图的意义。此时应考虑使用双Y轴或分开绘制。
# 示例6:堆叠面积图(预览概念)
# 假设我们有多渠道的每日流量数据
categories = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
direct = [120, 132, 101, 134, 90, 230, 210]
search_engine = [220, 182, 191, 234, 290, 330, 310]

stacked_area = (
    Line()
    .add_xaxis(categories)
    .add_yaxis("直接访问", direct,
               areastyle_opts=opts.AreaStyleOpts(opacity=0.8),
               stack="total") # 关键:指定堆叠组
    .add_yaxis("搜索引擎", search_engine,
               areastyle_opts=opts.AreaStyleOpts(opacity=0.8),
               stack="total") # 关键:指定相同的堆叠组
    .set_global_opts(
        title_opts=opts.TitleOpts(title="网站周流量来源(堆叠面积图)"),
        tooltip_opts=opts.TooltipOpts(trigger="axis")
    )
)

print("堆叠面积图已创建。'stack'参数将多个系列在垂直方向累加。")
堆叠面积图已创建。‘stack’参数将多个系列在垂直方向累加。

📝 动手练一练

  1. 数据转换练习:你有一个 pandas DataFrame df,包含 ‘timestamp’(时间戳,datetime类型)和 ‘temperature’(温度,float类型)两列。请写出将其转换为可用于 pyecharts Line 图表的 x_data 列表和 y_data 列表的代码。
点击查看参考答案
# 假设 df 已经存在
# 将 datetime 列格式化为字符串(例如'YYYY-MM-DD')
x_data = df['timestamp'].dt.strftime('%Y-%m-%d').tolist()
# 直接获取数值列
y_data = df['temperature'].tolist()
  1. 趋势歪曲识别:观察下面两段关于同一组销售数据的描述,哪个可能使用了不当的Y轴范围来误导观众?为什么?
    • A. “本季度销售额强劲增长50%!”(图表Y轴从本季度初的销售额数值开始)。
    • B. “本季度销售额稳步提升。”(图表Y轴从0开始)。
点击查看参考答案

描述 A 更可能使用了不当的Y轴范围。将Y轴起点设置为“本季度初的销售额数值”,会压缩图表的垂直显示范围,使得任何绝对的增长量在视觉上都被放大为更大的相对比例(如50%),从而可能夸大增长势头,误导观众认为增长非常“强劲”。描述B的表述“稳步提升”配合从0开始的Y轴,通常能更真实地反映增长的绝对规模。

本章小结

本节我们深入探讨了时间趋势可视化的两大基石:折线图与面积图。折线图擅长描绘精确的、连续的变化轨迹,是观察趋势、周期和异常点的首选。面积图则通过填充区域,强化了趋势的“体积感”或“累积量”,更适合传达规模而非单点数值。

📋 行动清单

  • 尝试转换:将你手头的一个包含时间序列的 pandas 数据集,用本节代码绘制成交互式折线图,并添加 DataZoom 功能。
  • 伦理自查:检查你或同事过去制作的趋势图表,Y轴的起点设置是否客观?是否存在无意或刻意的趋势歪曲?
  • 进阶探索:在 pyecharts 官方文档中搜索 AreaStyleOpts,了解如何为面积图配置渐变色彩(linear/radial),并尝试应用到你的图表中。

—— 小象教研组

配套资料 · 直接下载

课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。

  • 05_时间趋势可视化.ipynb · ECharts 课程配套资源
    IPYNB · 章级代码与输出
    下载
  • city_aqi.csv · ECharts 课程配套资源
    课程配套文件
    下载
  • sale_amount.csv · ECharts 课程配套资源
    课程配套文件
    下载