拿到一份新数据后,第一步不是急着分析,而是先摸清它的全貌:有几行几列?每列是什么类型?有没有缺失值?数值分布如何?本篇文章教你用 pandas 完成一套标准的"数据体检"流程(EDA,探索性数据分析)。
目录
1. EDA 是什么
2. 宏观概览:shape / info / head / tail
3. 数值统计:describe
4. 分布探索:value_counts / unique / nunique
5. 缺失值检查
6. 相关性分析
7. 分组快速对比
8. 一套完整的 EDA 模板
9. 常见坑与注意事项
10. 本章小结与练习
1. EDA 是什么
EDA(Exploratory Data Analysis,探索性数据分析) 指在正式建模或深入分析之前,用统计和可视化的手段快速了解数据的过程。pandas 提供了大量一行式方法,让 EDA 变得非常高效。
先造一份示例数据(本文所有示例都基于它):
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
"姓名": [f"用户{i}" for i in range(1, 101)],
"年龄": np.random.randint(18, 60, 100),
"城市": np.random.choice(["北京", "上海", "广州", "深圳", "杭州"], 100),
"消费金额": np.random.uniform(50, 5000, 100).round(2),
"是否会员": np.random.choice(["是", "否"], 100, p=[0.4, 0.6]),
})
df.loc[3, "年龄"] = np.nan # 制造一个缺失值
df.loc[7, "消费金额"] = np.nan
2. 宏观概览:shape / info / head / tail
2.1 形状
print(df.shape) # (100, 5):100 行 5 列
print(df.size) # 500
2.2 基本信息
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100 entries, 0 to 99
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 姓名 100 non-null object
1 年龄 99 non-null float64
2 城市 100 non-null object
3 消费金额 99 non-null float64
4 是否会员 100 non-null object
dtypes: float64(2), object(3)
memory usage: 4.0+ KB
一次看到:总行数、列数、每列非空个数、每列类型、内存占用。这是体检第一站。
2.3 前后几行
print(df.head()) # 前 5 行(默认)
print(df.head(10)) # 前 10 行
print(df.tail()) # 后 5 行
print(df.sample(5)) # 随机 5 行(检查数据是否乱序时好用)
2.4 列名与索引
print(df.columns) # 列名列表
print(df.index) # 行索引
3. 数值统计:describe
print(df.describe())
年龄 消费金额
count 99.000000 99.000000
mean 38.737374 2625.601313
std 12.038907 1421.344919
min 18.000000 62.540000
25% 29.000000 1411.510000
50% 39.000000 2635.920000
75% 49.000000 3894.300000
max 59.000000 4981.270000
| 统计量 | 含义 |
|--------|------|
| count | 非空个数(注意可能小于行数) |
| mean | 均值 |
| std | 标准差(离散程度) |
| min / max | 最小 / 最大值 |
| 25% / 50% / 75% | 四分位数(分布位置) |
变体用法:
# 只统计部分列
df[["年龄"]].describe()
# 自定义分位数
df.describe(percentiles=[0.1, 0.9])
# 字符串列统计
df.describe(include="object")
# 姓名 城市 是否会员
# count 100 100 100
# unique 100 5 2
# top 用户1 上海 否
# freq 1 26 60
# 所有列都看
df.describe(include="all")
4. 分布探索:value_counts / unique / nunique
4.1 分类列分布
# 城市分布(按次数降序)
print(df["城市"].value_counts())
# 带百分比
print(df["城市"].value_counts(normalize=True))
# 按字母排序显示
print(df["城市"].value_counts(ascending=True))
# 只看 top3 和"其他"
print(df["城市"].value_counts().head(3))
# 缺失值也统计进来
print(df["城市"].value_counts(dropna=False))
输出示例:
上海 26
杭州 21
广州 20
北京 17
深圳 16
Name: 城市, dtype: int64
4.2 唯一值
print(df["城市"].unique()) # 去重后的数组
print(df["城市"].nunique()) # 唯一值个数 = 5
print(df.nunique()) # 每列唯一值个数
4.3 数值列分箱看分布
# 将消费金额分成 4 档,看每档有多少人
bins = pd.cut(df["消费金额"], bins=4)
print(bins.value_counts())
# 指定分箱边界
bins2 = pd.cut(df["消费金额"], bins=[0, 1000, 3000, 5000], labels=["低", "中", "高"])
print(bins2.value_counts())
5. 缺失值检查
# 每列缺失值个数
print(df.isna().sum())
# 姓名 0
# 年龄 1
# 城市 0
# 消费金额 1
# 是否会员 0
# 缺失值占比
print(df.isna().mean().round(4) * 100)
# 哪些行有缺失
print(df[df.isna().any(axis=1)])
# 缺失值总数
print(df.isna().sum().sum())
缺失值的处理办法见第 7 章。
6. 相关性分析
对数值列之间做相关性矩阵,可以快速发现"哪些列一起变化":
# 数值列相关性矩阵
print(df[["年龄", "消费金额"]].corr())
# 全部数值列
print(df.corr(numeric_only=True))
# 与某列的相关性排序
print(df.corr(numeric_only=True)["消费金额"].sort_values(ascending=False))
年龄 消费金额
年龄 1.000000 0.051313
消费金额 0.051313 1.000000
| 相关系数 | 含义 |
|----------|------|
| 接近 1 | 正相关(同增同减) |
| 接近 -1 | 负相关(一增一减) |
| 接近 0 | 无明显线性关系 |
corr() 默认是 Pearson 相关系数。相关性 ≠ 因果性,请谨慎解读。7. 分组快速对比
# 各城市平均消费
print(df.groupby("城市")["消费金额"].mean().sort_values(ascending=False))
# 会员 vs 非会员:消费均值、中位数、人数
print(df.groupby("是否会员")["消费金额"].agg(["count", "mean", "median", "max"]))
# 交叉表:城市 × 是否会员
print(pd.crosstab(df["城市"], df["是否会员"]))
输出示例:
count mean median max
是否会员
否 60 2588.824833 2612.555000 4952.870000
是 39 2683.211282 2719.920000 4981.270000
groupby 的完整知识见第 13 章。
8. 一套完整的 EDA 模板
把下面的函数保存起来,任何 DataFrame 都能一键体检:
import pandas as pd
def quick_eda(df, n_sample=5):
"""快速体检:结构、缺失、数值统计、分类分布、相关性"""
print("=" * 50)
print("1. 数据形状:", df.shape)
print("=" * 50)
print("\n2. 基本信息:")
df.info()
print("\n3. 前几行:")
print(df.head(n_sample))
print("\n4. 缺失值统计:")
print(df.isna().sum())
num_cols = df.select_dtypes(include="number").columns.tolist()
if num_cols:
print("\n5. 数值列统计:")
print(df[num_cols].describe().T)
cat_cols = df.select_dtypes(include="object").columns.tolist()
if cat_cols:
print("\n6. 分类列分布:")
for col in cat_cols:
print(f"--- {col} ---")
print(df[col].value_counts())
if len(num_cols) >= 2:
print("\n7. 相关性矩阵:")
print(df[num_cols].corr())
# 使用
quick_eda(df)
9. 常见坑与注意事项
| 坑 | 现象 | 解决办法 |
|----|------|----------|
| describe 只统计数值列 | 字符串列"消失" | 加 include="all" 或 include="object" |
| count 不等于行数 | 有缺失值 | 对比 len(df) 与 df.count() |
| corr 报错 | 有非数值列 | 用 numeric_only=True 或先筛列 |
| value_counts 忽略 NaN | 分布"看起来"不完整 | 加 dropna=False |
| 数值列被读成 object | describe 不统计它 | 用 pd.to_numeric 转换(见第 8 章) |
10. 本章小结与练习
小结
- 宏观:
shape、info()、head/tail/sample; - 数值:
describe()看均值、标准差、四分位数; - 分类:
value_counts()看分布,unique/nunique看唯一性; - 缺失:
isna().sum()逐列体检; - 关系:
corr()看数值相关性; - 对比:
groupby + agg快速看分组差异。
练习题
1. 用 pd.read_csv 读入任意一份本地数据(或自己构造),执行 info() 和 describe()。
2. 检查每列缺失值个数与占比。
3. 找出唯一值最多的 3 列。
4. 用 value_counts(normalize=True) 观察分类列占比。
5. 计算两列数值的相关性并解读结果。
下一篇预告:第 6 章 数据选择与索引 —— 用 loc、iloc、布尔索引精准定位数据,这是 pandas 最常用的操作。
文章回复
0 条公开回复