数据清洗是数据分析流程中最耗时的环节,往往占据整个项目60%以上的时间。杂乱的数据中可能隐藏着缺失值、重复项、异常值和格式错误,直接影响分析结果的可靠性。Pandas作为Python数据分析的核心库,提供了丰富的工具来简化数据清洗工作。但很多初学者仍在用繁琐的循环处理数据,效率低下且易出错。本文将介绍Pandas中几种高效的数据清洗方法,帮助你快速处理常见的数据质量问题。
一、缺失值处理策略
缺失值是数据清洗中最常见的问题,Pandas提供了灵活的处理方案,关键在于根据数据特性选择合适的方法。
1. 识别缺失值
首先需要准确定位缺失值,Pandas中isna()
和notna()
方法可以高效检测:
import pandas as pd
import numpy as np# 创建含缺失值的示例数据
data = {'name': ['Alice', 'Bob', np.nan, 'David'],'age': [25, np.nan, 30, 35],'score': [88, 92, np.nan, 76]
}
df = pd.DataFrame(data)# 查看每列缺失值数量
print(df.isna().sum())
# 输出:
# name 1
# age 1
# score 1
# dtype: int64# 查看完整的缺失值位置
print(df[df.isna().any(axis=1)]) # 显示至少有一个缺失值的行
2. 处理缺失值
根据数据特点选择删除或填充策略:
# 1. 删除缺失值(适合缺失比例低的情况)
df_drop = df.dropna(subset=['name']) # 只删除name列有缺失的行# 2. 填充缺失值(更常用)
# 数值型列用均值填充
df['age'] = df['age'].fillna(df['age'].mean())# 类别型列用众数填充
df['name'] = df['name'].fillna(df['name'].mode()[0])# 用前一行的值填充(时间序列常用)
df['score'] = df['score'].fillna(method='ffill')print(df)
# 输出:
# name age score
# 0 Alice 25.0 88.0
# 1 Bob 30.0 92.0
# 2 Alice 30.0 92.0 # name用众数填充,age用均值(30)填充,score用前值填充
# 3 David 35.0 76.0
二、重复数据处理
重复数据会导致分析结果偏差,需要及时识别和处理。
# 创建含重复值的示例数据
df_dup = pd.DataFrame({'id': [1, 2, 2, 3, 3, 3],'value': [10, 20, 20, 30, 30, 30]
})# 检测重复行
print(df_dup.duplicated())
# 输出:
# 0 False
# 1 False
# 2 True # 与行1重复
# 3 False
# 4 True # 与行3重复
# 5 True # 与行3重复
# dtype: bool# 按指定列检测重复(如id列)
print(df_dup.duplicated(subset=['id']))# 删除重复行(保留第一行)
df_unique = df_dup.drop_duplicates(subset=['id'], keep='first')
print(df_unique)
# 输出:
# id value
# 0 1 10
# 1 2 20
# 3 3 30
三、数据格式标准化
原始数据常存在格式不统一的问题,如日期格式混乱、字符串大小写不一致等。
1. 日期格式标准化
# 处理混乱的日期格式
df_date = pd.DataFrame({'date': ['2023-10-05', '06/10/2023', '10-07-2023', '20231008'],'value': [100, 200, 300, 400]
})# 统一转换为datetime格式
df_date['date'] = pd.to_datetime(df_date['date'], infer_datetime_format=True)
print(df_date['date'])
# 输出:
# 0 2023-10-05
# 1 2023-06-10 # 注意:月/日/年格式会被正确解析
# 2 2023-10-07
# 3 2023-10-08
# Name: date, dtype: datetime64[ns]
2. 字符串格式标准化
# 处理字符串格式问题
df_str = pd.DataFrame({'city': ['beijing', 'SHANGHAI', 'guangzhou ', 'shenzhen'],'code': ['010', '021 ', ' 020', '0755']
})# 去除空格并统一为标题格式(首字母大写)
df_str['city'] = df_str['city'].str.strip().str.title()# 去除空格并保持大写
df_str['code'] = df_str['code'].str.strip().str.upper()print(df_str)
# 输出:
# city code
# 0 Beijing 010
# 1 Shanghai 021
# 2 Guangzhou 020
# 3 Shenzhen 0755
四、异常值检测与处理
异常值会严重影响统计分析结果,需要用科学方法识别。
1. 基于标准差的异常值检测(适合正态分布数据)
# 创建含异常值的示例数据
df_outlier = pd.DataFrame({'value': [10, 12, 11, 13, 12, 11, 100] # 100是异常值
})# 计算均值和标准差
mean = df_outlier['value'].mean()
std = df_outlier['value'].std()# 定义异常值边界(超出3个标准差)
lower_bound = mean - 3 * std
upper_bound = mean + 3 * std# 检测异常值
outliers = df_outlier[(df_outlier['value'] < lower_bound) | (df_outlier['value'] > upper_bound)]
print("异常值:", outliers)# 处理异常值(用边界值替换)
df_clean = df_outlier.copy()
df_clean['value'] = df_clean['value'].clip(lower_bound, upper_bound)
print("处理后数据:", df_clean['value'].tolist())
# 输出:[10, 12, 11, 13, 12, 11, 43.857] # 100被替换为上限值
2. 基于IQR的异常值检测(适合非正态分布数据)
# 计算四分位数
q1 = df_outlier['value'].quantile(0.25)
q3 = df_outlier['value'].quantile(0.75)
iqr = q3 - q1# 定义异常值边界
lower = q1 - 1.5 * iqr
upper = q3 + 1.5 * iqr# 过滤异常值
df_filtered = df_outlier[(df_outlier['value'] >= lower) & (df_outlier['value'] <= upper)]
五、批量数据转换
面对多列需要相同处理的场景,使用apply
和applymap
可以避免重复代码。
# 创建示例数据
df_multi = pd.DataFrame({'a': [' 100 ', ' 200 ', ' 300 '],'b': [' 400 ', ' 500 ', ' 600 '],'c': [' 700 ', ' 800 ', ' 900 ']
})# 批量处理:去除空格并转换为整数
df_multi = df_multi.apply(lambda x: x.str.strip().astype(int))
print(df_multi.dtypes)
# 输出:
# a int64
# b int64
# c int64
# dtype: object# 对数值列进行统一缩放(如归一化)
def normalize(col):return (col - col.min()) / (col.max() - col.min())df_scaled = df_multi.apply(normalize)
print(df_scaled)
六、实战案例:电商订单数据清洗
# 模拟电商订单数据
order_data = {'order_id': [1001, 1002, 1002, 1003, np.nan, 1005],'user_id': [1, 2, 2, 3, 4, 5],'amount': [99.9, 199.9, 199.9, '299.9', 399.9, 59999], # 含字符串和异常值'order_time': ['2023/11/01', '2023-11-02', '2023-11-02', '20231103', '2023-11-04', '2023-11-05'],'status': ['paid', 'paid', 'paid', 'unpaid', 'paid', 'paid']
}df_orders = pd.DataFrame(order_data)# 完整清洗流程
def clean_orders(df):# 1. 处理缺失值df = df.dropna(subset=['order_id']) # 删除order_id缺失的行# 2. 去除重复订单df = df.drop_duplicates(subset=['order_id'])# 3. 转换数据类型df['order_id'] = df['order_id'].astype(int)df['amount'] = pd.to_numeric(df['amount']) # 将字符串转换为数值df['order_time'] = pd.to_datetime(df['order_time'])# 4. 处理异常值(金额异常高的订单)q3 = df['amount'].quantile(0.75)iqr = df['amount'].quantile(0.75) - df['amount'].quantile(0.25)upper_limit = q3 + 1.5 * iqrdf['amount'] = df['amount'].clip(upper=upper_limit)return dfcleaned_orders = clean_orders(df_orders)
print(cleaned_orders)
总结
Pandas提供的向量化操作比传统循环快10-100倍,是高效数据清洗的关键。本文介绍的方法覆盖了80%的常见数据问题,核心原则包括:
- 优先使用Pandas内置方法,避免手动循环
- 缺失值处理需区分数值型和类别型数据
- 重复数据要根据业务主键检测
- 异常值处理需结合数据分布选择合适方法
- 批量处理多列时善用
apply
家族函数
数据清洗没有万能公式,需要结合业务理解选择合适的策略。建议清洗过程中每一步都保留中间结果,便于回溯和验证。熟练掌握这些技巧,能让你从繁琐的数据整理工作中解放出来,将更多精力投入到有价值的数据分析中。