在数据分析的实际工作中,原始数据往往存在缺失值、重复值、格式不规范、包含异常值等问题,直接使用这类数据进行分析会得到错误的结果。Pandas库提供了丰富的函数和方法,能够帮助我们快速完成数据清洗的全流程,同时针对不同类型的异常值也有对应的处理方案。

Pandas数据清洗常用技巧
1. 缺失值处理
缺失值是数据中最常见的问题之一,Pandas提供了isnull()和notnull()方法来判断缺失值,同时可以用dropna()删除缺失值,或者用fillna()填充缺失值。以下是具体的代码示例:
import pandas as pd
import numpy as np
# 构造包含缺失值的测试数据
data = {
'name': ['张三', '李四', '王五', '赵六', None],
'age': [20, 25, np.nan, 30, 28],
'score': [85, np.nan, 90, 88, 92]
}
df = pd.DataFrame(data)
print("原始数据:")
print(df)
# 判断缺失值
print("n缺失值判断结果:")
print(df.isnull())
# 删除包含缺失值的行
df_drop = df.dropna()
print("n删除缺失值后的数据:")
print(df_drop)
# 用均值填充年龄列的缺失值
age_mean = df['age'].mean()
df_fill = df.fillna({'age': age_mean, 'score': df['score'].median(), 'name': '未知'})
print("n填充缺失值后的数据:")
print(df_fill)
2. 重复值处理
重复值会导致分析结果出现偏差,Pandas的duplicated()方法可以标记重复行,drop_duplicates()方法可以删除重复行,示例代码如下:
# 构造包含重复值的测试数据
data_dup = {
'id': [1, 2, 2, 3, 4, 4],
'value': [10, 20, 20, 30, 40, 40]
}
df_dup = pd.DataFrame(data_dup)
print("包含重复值的数据:")
print(df_dup)
# 标记重复行
print("n重复行标记结果:")
print(df_dup.duplicated())
# 删除重复行,保留第一条
df_dup_clean = df_dup.drop_duplicates(keep='first')
print("n删除重复值后的数据:")
print(df_dup_clean)
3. 数据类型转换
有时候数据的类型不符合分析需求,比如数值被存为字符串类型,可以用astype()方法进行转换,代码如下:
# 构造类型不符合要求的数据
data_type = {
'price': ['100', '200', '300', '400']
}
df_type = pd.DataFrame(data_type)
print("原始数据类型:")
print(df_type.dtypes)
# 转换为数值类型
df_type['price'] = df_type['price'].astype(int)
print("n转换后的数据类型:")
print(df_type.dtypes)
异常值处理方法
异常值是指数据中偏离整体分布的极端值,会对分析结果产生较大干扰,常见的异常值处理方法有以下几种。
1. 基于3σ原则的异常值处理
3σ原则适用于符合正态分布的数据,认为数值偏离均值超过3倍标准差的概率为0.3%,属于小概率事件,这类数值可判定为异常值。以下是实现代码:
# 构造符合正态分布的测试数据
np.random.seed(42)
normal_data = np.random.normal(loc=50, scale=10, size=1000)
# 添加一个异常值
normal_data = np.append(normal_data, [150])
df_normal = pd.DataFrame({'value': normal_data})
# 计算均值和标准差
mean = df_normal['value'].mean()
std = df_normal['value'].std()
# 筛选正常数据
lower_bound = mean - 3 * std
upper_bound = mean + 3 * std
df_normal_clean = df_normal[(df_normal['value'] >= lower_bound) & (df_normal['value'] <= upper_bound)]
print("原始数据量:", len(df_normal))
print("清洗后数据量:", len(df_normal_clean))
2. 基于箱线图的异常值处理
箱线图通过四分位数来识别异常值,通常将小于Q1-1.5*IQR或者大于Q3+1.5*IQR的数值判定为异常值,这种方法不需要数据符合正态分布,适用性更广。代码如下:
# 使用上面的正态分布数据
q1 = df_normal['value'].quantile(0.25)
q3 = df_normal['value'].quantile(0.75)
iqr = q3 - q1
lower = q1 - 1.5 * iqr
upper = q3 + 1.5 * iqr
df_box_clean = df_normal[(df_normal['value'] >= lower) & (df_normal['value'] <= upper)]
print("箱线图方法清洗后数据量:", len(df_box_clean))
3. 异常值的处理策略
识别到异常值之后,可以根据实际场景选择不同的处理策略:
- 删除异常值:如果异常值是数据录入错误导致,且占比很小,可以直接删除
- 盖帽法处理:将异常值替换为指定的阈值,比如大于上限的替换为上限值,小于下限的替换为下限值
- 分箱处理:将连续值离散化,减少异常值的影响
以下是盖帽法的实现示例:
# 盖帽法处理异常值
df_cap = df_normal.copy()
df_cap.loc[df_cap['value'] > upper, 'value'] = upper
df_cap.loc[df_cap['value'] < lower, 'value'] = lower
print("盖帽法处理前的异常值:", df_normal[df_normal['value'] > upper]['value'].values)
print("盖帽法处理后的对应值:", df_cap[df_cap['value'] == upper]['value'].values)
实际应用注意事项
在实际的数据清洗工作中,不要盲目删除异常值,首先要确认异常值的产生原因,比如是否是业务上的正常波动,比如电商大促期间的销量突增就不属于需要处理的异常值。同时数据清洗的步骤要根据分析目标调整,比如做用户年龄分析时,年龄为负数的数值肯定是异常值,需要优先处理,而做销售额分析时,零值可能是正常的未成交情况,不需要直接删除。