什么是缺失值?¶
在数据分析中,缺失值(Missing Value)指的是数据集中某些行或列没有有效值。比如一份学生成绩表中,某学生的数学成绩没填,或者某公司某季度的销售额数据丢失了。在 pandas 中,缺失值通常用 NaN(Not a Number)表示,有时也会遇到 None,但 NaN 是更标准的数值型缺失值表示。
如何检查缺失值?¶
在处理缺失值前,第一步是找到数据中哪里有缺失值。pandas 提供了两个简单实用的方法:
1. isnull():标记缺失值¶
df.isnull() 会返回一个布尔型 DataFrame,True 表示该位置是缺失值,False 表示有效数据。
import pandas as pd
import numpy as np
# 创建一个带缺失值的示例 DataFrame
data = {
'姓名': ['小明', '小红', '小刚', '小丽', '小强'],
'年龄': [20, np.nan, 22, 21, np.nan],
'成绩': [85, 92, np.nan, 78, 88]
}
df = pd.DataFrame(data)
# 检查所有位置是否缺失
print(df.isnull())
输出结果:
姓名 年龄 成绩
0 False False False
1 False True False
2 False False True
3 False False False
4 False True False
2. isnull().sum():统计缺失值数量¶
df.isnull().sum() 可以快速统计每列的缺失值总数,方便定位问题。
# 统计每列缺失值数量
print(df.isnull().sum())
输出结果:
姓名 0
年龄 2
成绩 1
dtype: int64
3. info():查看数据整体缺失情况¶
df.info() 可以查看每列的数据类型和非空值数量,直观判断缺失值分布。
df.info()
输出结果:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5 entries, 0 to 4
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 姓名 5 non-null object
1 年龄 3 non-null float64
2 成绩 4 non-null float64
dtypes: float64(2), object(1)
memory usage: 240.0+ bytes
缺失值处理方法¶
处理缺失值的核心思路是:要么删除缺失值,要么用合理的值填充缺失值。
方法一:删除缺失值(dropna)¶
当缺失值很少且不影响整体数据时,可以直接删除包含缺失值的行或列。
1. 删除有缺失值的行(默认)¶
df.dropna(axis=0, how='any')
- axis=0:删除行(默认);axis=1:删除列。
- how='any':只要行/列中有一个缺失值就删除;how='all':仅当所有值都是缺失值时才删除。
# 删除所有有缺失值的行
df_drop_rows = df.dropna(axis=0)
print(df_drop_rows)
输出结果(仅保留无缺失值的行):
姓名 年龄 成绩
0 小明 20.0 85.0
3 小丽 21.0 78.0
2. 删除有缺失值的列¶
# 删除所有有缺失值的列
df_drop_cols = df.dropna(axis=1)
print(df_drop_cols)
输出结果(仅保留无缺失值的列):
姓名
0 小明
1 小红
2 小刚
3 小丽
4 小强
方法二:填充缺失值(fillna)¶
当缺失值较多或删除会丢失关键信息时,填充缺失值更常用。pandas 的 fillna() 支持多种填充方式。
1. 用固定值填充¶
直接用某个固定值(如 0、-1)填充所有缺失值。
# 用 0 填充所有缺失值
df_fill_0 = df.fillna(0)
print(df_fill_0)
输出结果:
姓名 年龄 成绩
0 小明 20.0 85.0
1 小红 0.0 92.0
2 小刚 22.0 0.0
3 小丽 21.0 78.0
4 小强 0.0 88.0
2. 用统计量填充(均值/中位数/众数)¶
数值型数据适合用统计量填充,比如:
- 均值:适合对称分布数据(如正态分布)。
- 中位数:适合偏态分布数据(抗极端值能力强)。
- 众数:适合分类数据(出现次数最多的值)。
# 用年龄列的均值填充年龄缺失值
df['年龄'] = df['年龄'].fillna(df['年龄'].mean())
print(df['年龄'])
输出结果(年龄列均值为 (20+22+21)/3=21):
0 20.0
1 21.0
2 22.0
3 21.0
4 21.0
Name: 年龄, dtype: float64
3. 向前/向后填充(ffill/bfill)¶
适用于时间序列数据或有顺序的数据(如按时间排列的销售额)。
- ffill():用前一个非缺失值填充后面的缺失值。
- bfill():用后一个非缺失值填充前面的缺失值。
# 模拟时间序列数据
df_time = pd.DataFrame({'value': [1, np.nan, 3, np.nan, 5]})
# 向前填充
df_forward = df_time.fillna(method='ffill')
print("向前填充结果:")
print(df_forward)
# 向后填充
df_backward = df_time.fillna(method='bfill')
print("\n向后填充结果:")
print(df_backward)
输出结果:
向前填充结果:
value
0 1.0
1 1.0
2 3.0
3 3.0
4 5.0
向后填充结果:
value
0 1.0
1 3.0
2 3.0
3 5.0
4 5.0
综合实践案例¶
假设我们有一份电商订单数据,需要处理缺失值。
数据准备¶
# 订单数据
orders = {
'订单ID': [101, 102, 103, 104, 105],
'客户姓名': ['A', 'B', 'C', 'D', 'E'],
'金额': [150, np.nan, 200, np.nan, 80],
'支付方式': ['微信', '支付宝', np.nan, '微信', '支付宝']
}
df_orders = pd.DataFrame(orders)
步骤1:检查缺失值¶
print(df_orders.isnull().sum())
输出:
订单ID 0
客户姓名 0
金额 2
支付方式 1
dtype: int64
步骤2:处理缺失值¶
- 金额列:数值型,用均值填充。
- 支付方式列:分类型,用众数(出现次数最多的值)填充。
# 填充金额列的缺失值(均值)
df_orders['金额'] = df_orders['金额'].fillna(df_orders['金额'].mean())
# 填充支付方式列的缺失值(众数)
df_orders['支付方式'] = df_orders['支付方式'].fillna(df_orders['支付方式'].mode()[0])
print(df_orders)
输出结果:
订单ID 客户姓名 金额 支付方式
0 101 A 150.0 微信
1 102 B 143.333333 支付宝 # 均值=(150+200+80)/3≈143.33
2 103 C 200.0 支付宝 # 众数是支付宝(出现2次)
3 104 D 143.333333 微信
4 105 E 80.0 支付宝
总结¶
缺失值处理的核心步骤:
1. 检查缺失值:用 isnull() 和 isnull().sum() 定位问题。
2. 选择处理策略:
- 缺失值极少(<5%)且不重要 → 删除(dropna())。
- 缺失值较多或关键数据 → 填充(fillna()),优先用统计量(均值/中位数)或固定值。
3. 验证结果:处理后再次检查数据,确保缺失值已合理处理。
处理缺失值没有“万能方法”,需结合数据特点和业务需求灵活选择,避免因处理不当导致数据偏差。