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

数据可视化的目标和作用

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

在上一节,我们了解了数据分析与可视化的基本概念。本节我们将聚焦于一个核心问题:我们为什么要做数据可视化?它究竟能为我们带来哪些不可替代的价值?我们将通过一个经典的统计学案例——“安斯库姆四重奏”,来直观地揭示可视化在洞察数据本质、避免分析陷阱方面的关键作用。

💡 核心导读

  • 目标总结:明确数据可视化的四大核心目标:呈现复杂结果、辅助精准决策、加深理解记忆、高效传递信息。
  • 经典案例:学习“安斯库姆四重奏”案例,理解仅依赖统计摘要(如均值、方差)分析数据可能带来的误导。
  • 可视化威力:通过绘制散点图,直观展示四组统计特性一致的数据背后截然不同的分布模式。
  • 实践过渡:本节将使用 Python 进行数据探索和初步可视化,为后续深入学习基于 Web 的交互式图表(ECharts/pyecharts)打下基础。

一、可视化的核心目标与作用

数据可视化远不止是“让图表更好看”。它是一种强大的沟通和分析工具,其核心目标可以总结为以下四点:

  1. 呈现复杂分析结果:将多维、复杂的分析结果,通过丰富的图表形式直观地呈现给读者,降低理解门槛。
  2. 辅助精准决策:让决策者能够快速、精准地洞察数据反映的趋势、占比、异常等关键信息,而无需费力解读枯燥的表格数字。
  3. 加深理解与记忆:“一图胜千言”。视觉化的信息比纯文本或数字更符合人类认知习惯,能有效加深受众对数据的理解和长期记忆。
  4. 高效传递信息与知识:以准确、高效、精简且全面的方式,在团队或受众间传递核心信息和知识,提升协作与沟通效率。

其中,第四点“准确而高效”尤为重要。它强调可视化不仅是“高效”的,更必须是“准确”的,要能真实反映数据背后的故事,避免因不当的分析方法而产生误导。接下来,我们将通过一个经典案例来深刻体会这一点。

二、经典案例:安斯库姆四重奏

“安斯库姆四重奏”(Anscombe‘s quartet)是统计学家弗朗西斯·安斯库姆(Francis Anscombe)于 1973 年构造的四组数据。它用一个极其巧妙的方式证明了:在分析数据之前先进行可视化检查是至关重要的,同时也揭示了离群值对统计结果的巨大影响。

这四组数据各有 11 个 (x, y) 数据点。如果我们仅计算一些常见的统计量,会得到一个惊人的结论。

让我们把安斯库姆四重奏的 44 个点显式写进 pandas DataFrame,不使用额外的统计绘图库。这样既能保证数据可复现,也方便后续直接交给 pyecharts。

import pandas as pd

base_x = [10, 8, 13, 9, 11, 14, 6, 4, 12, 7, 5]
iv_x = [8, 8, 8, 8, 8, 8, 8, 19, 8, 8, 8]
series_y = {
    "I": [8.04, 6.95, 7.58, 8.81, 8.33, 9.96, 7.24, 4.26, 10.84, 4.82, 5.68],
    "II": [9.14, 8.14, 8.74, 8.77, 9.26, 8.10, 6.13, 3.10, 9.13, 7.26, 4.74],
    "III": [7.46, 6.77, 12.74, 7.11, 7.81, 8.84, 6.08, 5.39, 8.15, 6.42, 5.73],
    "IV": [6.58, 5.76, 7.71, 8.84, 8.47, 7.04, 5.25, 12.50, 5.56, 7.91, 6.89],
}

rows = []
for dataset, y_values in series_y.items():
    x_values = iv_x if dataset == "IV" else base_x
    rows.extend({"dataset": dataset, "x": x, "y": y} for x, y in zip(x_values, y_values))

anscombe_df = pd.DataFrame(rows)
print("数据集行数:", len(anscombe_df))
print("数据集预览(前12行):")
print(anscombe_df.head(12).to_string(index=False))
anscombe_df.info()

summary_stats = anscombe_df.groupby("dataset").agg(
    x_mean=("x", "mean"),
    x_var=("x", "var"),
    y_mean=("y", "mean"),
    y_var=("y", "var"),
)
summary_stats["correlation"] = anscombe_df.groupby("dataset")[["x", "y"]].apply(
    lambda frame: frame["x"].corr(frame["y"])
)
print("四组数据的统计摘要:")
print(summary_stats.round(2).to_string())
数据集行数: 44
数据集预览(前12行):
dataset  x     y
      I 10  8.04
      I  8  6.95
      I 13  7.58
      I  9  8.81
      I 11  8.33
      I 14  9.96
      I  6  7.24
      I  4  4.26
      I 12 10.84
      I  7  4.82
      I  5  5.68
     II 10  9.14

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 44 entries, 0 to 43
Data columns (total 3 columns):
 #   Column   Non-Null Count  Dtype  
---  ------   --------------  -----  
 0   dataset  44 non-null     str    
 1   x        44 non-null     int64  
 2   y        44 non-null     float64
dtypes: float64(1), int64(1), str(1)
memory usage: 1.2 KB
四组数据的统计摘要:
         x_mean  x_var  y_mean  y_var  correlation
dataset                                           
I           9.0   11.0     7.5   4.13         0.82
II          9.0   11.0     7.5   4.13         0.82
III         9.0   11.0     7.5   4.12         0.82
IV          9.0   11.0     7.5   4.12         0.82

数据包含三列:dataset(数据集编号 I, II, III, IV)、x 和 y。总共 44 行,即每个数据集 11 个点。

上面的同一段代码已经同时计算了每组数据的 x 均值、y 均值、方差和相关系数,避免把统计与数据准备拆成互相依赖的两个代码块。

惊人的发现:从统计摘要来看,这四组数据几乎一模一样!

  • x 的均值都是 9.0
  • x 的方差都是 11.0
  • y 的均值都是 7.5
  • y 的方差都是 ~4.13
  • x 与 y 的相关系数都是 ~0.82

如果仅凭这些数字做判断,我们很可能会认为这四组数据描述了相同的现象或关系。但事实果真如此吗?下面用 pyecharts 画出四组散点图,直接观察分布形态。

import pandas as pd
from pyecharts import options as opts
from pyecharts.charts import Page, Scatter
from pyecharts.globals import CurrentConfig

CurrentConfig.ONLINE_HOST = "assets/"

base_x = [10, 8, 13, 9, 11, 14, 6, 4, 12, 7, 5]
iv_x = [8, 8, 8, 8, 8, 8, 8, 19, 8, 8, 8]
series_y = {
    "I": [8.04, 6.95, 7.58, 8.81, 8.33, 9.96, 7.24, 4.26, 10.84, 4.82, 5.68],
    "II": [9.14, 8.14, 8.74, 8.77, 9.26, 8.10, 6.13, 3.10, 9.13, 7.26, 4.74],
    "III": [7.46, 6.77, 12.74, 7.11, 7.81, 8.84, 6.08, 5.39, 8.15, 6.42, 5.73],
    "IV": [6.58, 5.76, 7.71, 8.84, 8.47, 7.04, 5.25, 12.50, 5.56, 7.91, 6.89],
}
rows = []
for dataset, y_values in series_y.items():
    x_values = iv_x if dataset == "IV" else base_x
    rows.extend({"dataset": dataset, "x": x, "y": y} for x, y in zip(x_values, y_values))
anscombe_df = pd.DataFrame(rows)

page = Page(layout=Page.SimplePageLayout)
for dataset, group in anscombe_df.groupby("dataset"):
    chart = (
        Scatter()
        .add_xaxis(group["x"].tolist())
        .add_yaxis(f"数据集 {dataset}", group["y"].tolist())
        .set_global_opts(
            title_opts=opts.TitleOpts(title=f"安斯库姆四重奏 · 数据集 {dataset}"),
            xaxis_opts=opts.AxisOpts(type_="value", name="x"),
            yaxis_opts=opts.AxisOpts(type_="value", name="y"),
        )
    )
    page.add(chart)

page.render("anscombe_quartet.html")
安斯库姆四重奏四组散点图,四组数据分布形状各不相同
图:安斯库姆四重奏:四组数据的散点形状完全不同,但均值、方差和相关系数几乎一致。手机端可左右滑动查看,点击图片可看全图。

代码会生成 anscombe_quartet.html,包含数据集 I、II、III、IV 的四张散点图。

视觉化揭示的真相:

  • 数据集 I:看起来是“标准”的线性正相关,点大致沿直线分布。
  • 数据集 II:呈现出明显的非线性关系(类似抛物线),但用直线去拟合会严重误导。
  • 数据集 III:整体呈线性趋势,但存在一个明显的离群值。这个离群值极大地影响了回归线的位置。
  • 数据集 IV:x 值几乎都集中在 8,只有一个点 x=19 远离其他点。这个极端的离群值“创造”出了虚假的高相关系数。

三、案例启示与可视化的重要性

安斯库姆四重奏给我们上了生动的一课:

  1. 统计摘要的局限性:均值、方差、相关系数等汇总统计量虽然简洁,但会丢失数据的分布形态、非线性关系以及离群值等关键信息,可能导致完全错误的结论。
  2. 可视化是不可或缺的探索步骤:在正式建模或下结论前,必须对数据进行可视化探索。图形能直观揭示数据中隐藏的模式、关系和问题,这是任何数字表格都无法替代的。
  3. 离群值需要特别关注:离群值(异常点)可能代表数据录入错误、测量误差,也可能代表有重大意义的特殊现象。可视化能帮助我们快速识别它们,并决定如何处理。

这正是数据可视化“准确而高效”传递信息的体现。它确保了我们在“高效”获取洞察的同时,不会因为方法缺陷而偏离“准确”的事实。

在本课程中,我们将学习使用 pyecharts(基于 ECharts)来创建交互式、可嵌入 Web 页面的图表。与上面演示的静态图相比,交互式图表允许用户缩放、拖拽、悬停查看数值,并能组合成复杂的仪表盘,在“数据大屏”等场景下发挥巨大威力。从下节课开始,我们将正式进入 pyecharts 的世界。

📝 动手练一练

  1. 数据提取:在不使用 groupby 的情况下,编写代码分别提取出安斯库姆四重奏中“数据集 III”的所有数据点,并打印其 x 和 y 的值。
点击查看参考答案
# 方法1:使用布尔索引
dataset_iii = anscombe_df[anscombe_df['dataset'] == 'III']
print("数据集 III 的数据点:")
print(dataset_iii[['x', 'y']].to_string(index=False))

# 方法2:使用query方法
dataset_iii_query = anscombe_df.query("dataset == 'III'")
print("\n使用query方法得到的数据集 III:")
print(dataset_iii_query[['x', 'y']].values)
数据集 III 的数据点:
    x     y
 10.0  7.46
  8.0  6.77
 13.0 12.74
  9.0  7.11
 11.0  7.81
 14.0  8.84
  6.0  6.08
  4.0  5.39
 12.0  8.15
  7.0  6.42
  5.0  5.73

使用query方法得到的数据集 III:
[[10.   7.46]
 [ 8.   6.77]
 [13.  12.74]
 [ 9.   7.11]
 [11.   7.81]
 [14.   8.84]
 [ 6.   6.08]
 [ 4.   5.39]
 [12.   8.15]
 [ 7.   6.42]
 [ 5.   5.73]]
  1. 离群值影响:手动计算数据集 III 在包含和不包含离群值(你认为哪个点是离群值?)两种情况下的 y 的均值,体会离群值对统计量的影响。
点击查看参考答案
# 提取数据集 III
data_iii = anscombe_df[anscombe_df['dataset'] == 'III'].copy()
# 假设我们认为 (13.0, 12.74) 是离群值(y值明显偏大)
outlier_index = data_iii[data_iii['x'] == 13.0].index[0]
data_iii_without_outlier = data_iii.drop(index=outlier_index)

mean_y_with = data_iii['y'].mean()
mean_y_without = data_iii_without_outlier['y'].mean()

print(f"包含离群值时 y 的均值: {mean_y_with:.2f}")
print(f"去除离群值后 y 的均值: {mean_y_without:.2f}")
print(f"离群值使均值增大了: {mean_y_with - mean_y_without:.2f}")
包含离群值时 y 的均值: 7.50
去除离群值后 y 的均值: 6.98
离群值使均值增大了: 0.52

本章小结

本节我们深入探讨了数据可视化的根本目标,并通过“安斯库姆四重奏”这一经典案例,直观感受到了可视化在数据探索与分析中的决定性作用。记住,图表不仅是结果的展示,更是发现真相的工具。

📋 行动清单

  • 理解并复述数据可视化的四大核心作用。
  • 运行并理解安斯库姆四重奏的代码,观察统计摘要与可视化图形的巨大差异。
  • 思考在你过去或当前的项目中,是否有仅依赖数字表格做决策而可能忽略数据真实分布的情况?

—— 小象教研组