第三章 数据探索与可视化:从单变量到多变量的全景洞察

数据探索,说白了就是跟你的数据「混个脸熟」。我见过太多人一上来就调模型,结果跑出来的结果连自己都不敢信——为什么?因为根本不了解数据长什么样。这一章,我们就来聊聊怎么用可视化的手段,把数据的脾气摸透。

3.1 单变量分析:先看看每个特征自己长啥样

单变量分析,就是一次只看一个变量。别觉得简单,这一步往往能发现大问题。我个人习惯,拿到数据后第一件事就是画直方图和箱线图。

3.4.1 直方图:分布的秘密

直方图能告诉你数据是正态分布、偏态分布,还是多峰分布。举个例子:

import matplotlib.pyplot as plt
import seaborn as sns

# 假设 df 是你的 DataFrame
plt.figure(figsize=(10, 6))
sns.histplot(df['收益率'], bins=50, kde=True)
plt.title('收益率分布直方图')
plt.xlabel('收益率')
plt.ylabel('频数')
plt.show()

我在项目中遇到过一件事:某次分析股票日收益率,直方图画出来一看,尾部特别厚。嗯,这就是典型的「肥尾分布」,说明极端行情比正态分布预测的要频繁得多。如果你忽略这一点,VaR模型算出来就会严重低估风险。

注意: bins 的数量很关键。太少会丢失细节,太多会引入噪声。我一般从 30-50 开始试,根据数据量调整。

3.4.2 箱线图:异常值无处遁形

箱线图是发现异常值的神器。它展示了中位数、四分位数和异常值。

plt.figure(figsize=(10, 6))
sns.boxplot(x=df['市盈率'])
plt.title('市盈率箱线图')
plt.show()

你想想看,如果某个股票的市盈率远高于上边缘,那它要么是成长性极好,要么就是数据录入错误。我曾经就因为箱线图发现了一个「市盈率=9999」的脏数据——原来是缺失值被填成了9999。

小技巧: 箱线图的异常值判定规则是 Q1 - 1.5*IQR 和 Q3 + 1.5*IQR。但金融数据往往尾部厚,我建议用 3*IQR 作为阈值,否则会误杀太多正常值。

3.2 双变量分析:两个变量之间有什么猫腻?

单变量看完了,接下来就要看变量之间的关系。双变量分析的核心就是回答一个问题:A变了,B会不会跟着变?

3.2.1 散点图:最直观的关系图

散点图能直接看出两个变量是正相关、负相关,还是没有关系。

plt.figure(figsize=(10, 6))
sns.scatterplot(x=df['成交量'], y=df['涨跌幅'], alpha=0.5)
plt.title('成交量 vs 涨跌幅')
plt.xlabel('成交量')
plt.ylabel('涨跌幅')
plt.show()

为什么要加 alpha=0.5?因为金融数据点经常重叠,不加透明度你看到的只是一团黑。我记得有一次画沪深300成分股的散点图,不加alpha根本看不出分布密度,加了之后才发现大部分点集中在低成交量区域。

核心要点: 散点图如果呈现「喇叭形」或「漏斗形」,说明存在异方差性。这在回归模型中是个大问题,需要处理。

3.2.2 相关系数矩阵:数字化的关系度量

散点图靠眼睛看,相关系数矩阵则给出精确的数字。

import pandas as pd
import numpy as np

# 计算相关系数矩阵
corr_matrix = df[['收益率', '成交量', '市盈率', '市净率']].corr()

# 可视化
plt.figure(figsize=(8, 6))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', fmt='.2f')
plt.title('相关系数热力图')
plt.show()

这里有个坑:相关系数只度量线性关系。如果两个变量是U型关系,相关系数可能接近0,但实际关系很强。我建议先画散点图,再看相关系数,两者结合才不会误判。

相关系数范围 相关强度 实际含义
0.8 - 1.0 极强相关 小心多重共线性
0.5 - 0.8 强相关 可能有信息重叠
0.3 - 0.5 中等相关 值得进一步探索
0.0 - 0.3 弱相关或无相关 可能独立

3.3 多变量分析:全局视角下的数据格局

两个变量看完了,但现实中的数据往往是高维的。多变量分析就是帮你从全局视角理解数据。

3.3.1 平行坐标图:高维数据的「高速公路」

平行坐标图把每个特征作为一条垂直轴,每个样本就是一条穿过这些轴的折线。它能帮你发现不同类别在多个维度上的分布模式。

from pandas.plotting import parallel_coordinates

# 假设有一个 '类别' 列
plt.figure(figsize=(12, 6))
parallel_coordinates(df, class_column='类别', cols=['收益率', '成交量', '市盈率', '市净率'])
plt.title('平行坐标图')
plt.show()

说实话,平行坐标图在特征少于10个时最好用。特征太多,线条会乱成一团。我一般用它来快速验证:不同类别的样本在哪些维度上分得开。

3.3.2 热力图:全局相关性一目了然

热力图我们已经在上面的相关系数矩阵中用过了。但多变量分析中的热力图,可以展示更多信息,比如缺失值模式、聚类结果等。

# 缺失值热力图
plt.figure(figsize=(10, 8))
sns.heatmap(df.isnull(), cbar=False, cmap='viridis')
plt.title('缺失值分布热力图')
plt.show()

白色区域就是缺失值。如果某列全是白色,那这个特征基本废了。如果某行全是白色,那这个样本可以直接扔掉。我曾经用这个图发现了一个规律:某只股票的「市净率」缺失总是伴随着「市盈率」也缺失——后来查明是因为数据源的问题。

3.4 自动化EDA:Pandas Profiling 让你事半功倍

手动画图固然好,但如果你有几十个特征,一个个画太慢了。这时候,Pandas Profiling 就是你的救星。

from pandas_profiling import ProfileReport

# 生成报告
profile = ProfileReport(df, title='EDA报告', explorative=True)

# 保存为HTML
profile.to_file("eda_report.html")

这个工具会自动生成:

  • 每个变量的统计摘要(均值、标准差、分位数等)
  • 直方图和箱线图
  • 缺失值统计
  • 变量之间的相关系数
  • 高频值列表
我的习惯: 先用 Pandas Profiling 跑一遍全局报告,快速定位问题。然后针对有问题的变量,再手动画图深入分析。这样既高效又不遗漏细节。
注意: 数据量大的时候(超过10万行),Pandas Profiling 会非常慢。我建议先抽样,或者用它的最小模式:ProfileReport(df, minimal=True)

3.5 本章知识体系

下面这张图总结了数据探索与可视化的核心逻辑,从单变量到多变量,从手动到自动化:

数据探索与可视化知识体系 原始数据 单变量分析 双变量分析 多变量分析 直方图 箱线图 统计摘要 散点图 相关系数矩阵 热力图 平行坐标图 全局热力图 聚类分析 Pandas Profiling 自动化EDA 洞察报告 / 特征工程决策

数据探索不是一次性工作。我建议你每次新增特征后,都重新跑一遍可视化流程。你会发现,很多因子失效的苗头,在图上早就暴露了。

总结一句话: 可视化不是花架子,它是你理解数据、发现规律、避免踩坑的第一道防线。别偷懒,画图吧。

专注资料整理