Pandas-数据清洗与处理
Pandas-数据清洗与处理
- 一、数据清洗的核心目标
- 二、缺失值处理
- 1. 缺失值检测
- 2. 缺失值处理策略
- (1)删除法
- (2)填充法
- 三、异常值识别与处理
- 1. 异常值检测方法
- (1)统计法
- (2)业务规则法
- 2. 异常值处理策略
- 四、重复数据处理
- 1. 重复数据检测
- 2. 重复数据处理
- 五、数据格式转换与标准化
- 1. 日期时间格式转换
- 2. 文本数据清洗
- 3. 数据类型转换
- 六、应用实例:完整数据清洗流程
数据清洗与处理往往占据最多的时间,现实世界中的数据很少是完美的——它们可能包含缺失值、异常值、重复记录,或是格式混乱、命名不规范的字段。Pandas作为Python数据处理的瑞士军刀,提供了强大而灵活的工具来应对这些问题。
一、数据清洗的核心目标
数据清洗的最终目的是提升数据质量,为后续分析和建模奠定基础。具体来说,需要实现以下目标:
- 完整性:处理缺失值,确保关键信息不缺失
- 一致性:统一数据格式、命名规范和取值标准
- 准确性:识别并修正异常值、错误数据
- 唯一性:去除重复记录,避免分析结果偏差
- 可用性:将数据转换为便于分析的结构(如日期类型、分类编码)
围绕这些目标,Pandas提供了从简单到复杂的一系列处理函数,掌握它们的组合使用是高效清洗数据的关键。
二、缺失值处理
缺失值是数据清洗中最常见的问题,Pandas通过isnull()
、dropna()
和fillna()
等函数形成完整的处理链条。
1. 缺失值检测
首先需要定位缺失值的位置和比例:
import pandas as pd
import numpy as np# 读取示例数据
df = pd.read_csv('messy_data.csv')# 检测每列缺失值数量及比例
missing_count = df.isnull().sum()
missing_ratio = (missing_count / len(df)).round(3)
missing_df = pd.DataFrame({'缺失值数量': missing_count,'缺失比例': missing_ratio
})
print("缺失值统计:")
print(missing_df[missing_df['缺失值数量'] > 0]) # 只显示有缺失的列
2. 缺失值处理策略
根据缺失比例和字段重要性,选择不同的处理方式:
(1)删除法
适用于缺失比例极高(如超过80%)或对分析无影响的字段,或缺失行数量极少的情况:
# 删除缺失比例超过50%的列
threshold = len(df) * 0.5
df = df.dropna(thresh=threshold, axis=1)# 删除关键字段(如用户ID)缺失的行
df = df.dropna(subset=['user_id', 'order_date'])
(2)填充法
对于重要字段,需根据字段类型选择合理的填充值:
# 数值型字段:用中位数填充(抗异常值能力强于均值)
df['amount'] = df['amount'].fillna(df['amount'].median())# 分类型字段:用众数或特殊标记(如"未知")填充
df['category'] = df['category'].fillna(df['category'].mode()[0])
df['city'] = df['city'].fillna('未知城市')# 时间型字段:用前后值填充(适用于时间序列数据)
df['login_time'] = df['login_time'].fillna(method='ffill') # 向前填充
df['logout_time'] = df['logout_time'].fillna(method='bfill') # 向后填充# 分组填充:按类别分组后,用组内均值填充(更精准)
df['score'] = df.groupby('class')['score'].transform(lambda x: x.fillna(x.mean())
)
三、异常值识别与处理
异常值(离群点)会扭曲统计结果和模型训练,需结合业务逻辑识别并处理。
1. 异常值检测方法
(1)统计法
适用于数值型字段,通过四分位距(IQR)或标准差判断:
def detect_outliers_iqr(df, col):"""用IQR法检测异常值"""q1 = df[col].quantile(0.25)q3 = df[col].quantile(0.75)iqr = q3 - q1lower_bound = q1 - 1.5 * iqrupper_bound = q3 + 1.5 * iqrreturn (df[col] < lower_bound) | (df[col] > upper_bound)# 检测"金额"字段的异常值
df['is_amount_outlier'] = detect_outliers_iqr(df, 'amount')
print(f"异常值比例:{df['is_amount_outlier'].mean():.2%}")
(2)业务规则法
基于领域知识判断,如订单金额不能为负、年龄不能超过150岁:
# 检测金额为负的异常值
df['is_invalid_amount'] = df['amount'] < 0# 检测年龄不合理的记录
df['is_invalid_age'] = (df['age'] < 0) | (df['age'] > 150)
2. 异常值处理策略
# 1. 修正明显错误(如金额为负可能是符号错误)
df.loc[df['amount'] < 0, 'amount'] = df.loc[df['amount'] < 0, 'amount'].abs()# 2. 截断法(将异常值限制在合理范围内)
q1, q3 = df['amount'].quantile([0.25, 0.75])
df['amount'] = df['amount'].clip(lower=q1 - 1.5 * (q3 - q1), upper=q3 + 1.5 * (q3 - q1))# 3. 标记法(保留异常值但标记,供后续分析)
df['age'] = df['age'].mask(df['age'] > 120, np.nan) # 用NaN标记异常年龄,后续填充
四、重复数据处理
重复数据可能导致分析结果偏误(如重复计算同一订单),需检测并去重。
1. 重复数据检测
# 检测完全重复的行
duplicate_rows = df.duplicated().sum()
print(f"完全重复的行数:{duplicate_rows}")# 检测关键字段组合重复(如同一用户同一时间的重复记录)
duplicate_combo = df.duplicated(subset=['user_id', 'login_time'], keep=False).sum()
print(f"用户-时间组合重复的记录数:{duplicate_combo}")# 查看重复样本
if duplicate_rows > 0:print("重复样本示例:")print(df[df.duplicated(keep=False)].head())
2. 重复数据处理
# 保留第一次出现的记录,删除后续重复行
df = df.drop_duplicates(keep='first')# 对关键字段组合去重,并保留金额最大的记录
df = df.sort_values('amount', ascending=False) # 按金额降序排列
df = df.drop_duplicates(subset=['user_id', 'product_id'], keep='first')
五、数据格式转换与标准化
原始数据常存在格式混乱问题(如日期格式不统一、文本大小写混用),需标准化处理。
1. 日期时间格式转换
日期字段若以字符串形式存储,需转换为datetime
类型才能进行时间运算:
# 转换日期字符串为datetime类型(支持多种格式自动识别)
df['order_date'] = pd.to_datetime(df['order_date'], errors='coerce')
# errors='coerce'将无效格式转为NaT(缺失日期)# 提取日期中的年、月、周等信息
df['order_year'] = df['order_date'].dt.year
df['order_month'] = df['order_date'].dt.month
df['is_weekend'] = df['order_date'].dt.dayofweek.isin([5, 6]) # 判断是否周末
2. 文本数据清洗
处理文本字段中的大小写、空格、特殊字符等问题:
# 去除字符串前后空格
df['product_name'] = df['product_name'].str.strip()# 统一为小写(适用于不区分大小写的场景)
df['category'] = df['category'].str.lower()# 替换特殊字符(如将"手机_"和"手机("统一为"手机")
df['category'] = df['category'].str.replace(r'[_\(\)]', '', regex=True)# 提取文本中的数字(如从"价格:99元"中提取99)
df['price'] = df['price_text'].str.extract(r'(\d+)').astype(float)
3. 数据类型转换
优化数据类型以减少内存占用,或满足分析需求:
# 将字符串ID转换为分类类型(适用于高基数但重复值多的字段)
df['user_id'] = df['user_id'].astype('category')# 将数值型字符串转换为数值类型(如"100.5"→100.5)
df['amount'] = pd.to_numeric(df['amount_str'], errors='coerce')# 将布尔值转换为0/1(便于某些模型处理)
df['is_vip'] = df['is_vip'].astype(int)
六、应用实例:完整数据清洗流程
def clean_data(df):"""完整的数据清洗函数"""print(f"原始数据形状:{df.shape}")# 1. 缺失值处理print("\n=== 处理缺失值 ===")# 删除无意义的高缺失列df = df.drop(columns=['unused_col1', 'unused_col2'])# 填充数值列和分类型列num_cols = df.select_dtypes(include=['int64', 'float64']).columnsfor col in num_cols:df[col] = df[col].fillna(df[col].median())cat_cols = df.select_dtypes(include=['object', 'category']).columnsfor col in cat_cols:df[col] = df[col].fillna(df[col].mode()[0] if not df[col].mode().empty else '未知')# 2. 异常值处理print("\n=== 处理异常值 ===")df['amount'] = df['amount'].clip(lower=0) # 金额不能为负df['age'] = df['age'].mask((df['age'] < 0) | (df['age'] > 120), df['age'].median())# 3. 重复数据处理print("\n=== 处理重复数据 ===")df = df.drop_duplicates(subset=['order_id'], keep='first')# 4. 格式转换print("\n=== 格式转换 ===")df['order_date'] = pd.to_datetime(df['order_date'], errors='coerce')df['product_name'] = df['product_name'].str.strip().str.lower()print(f"清洗后数据形状:{df.shape}")return df# 执行清洗流程
cleaned_df = clean_data(df)
总结:数据清洗的核心原则
- 理解业务:数据清洗的前提是理解字段含义和业务逻辑(如“年龄为负”在业务中一定是错误)。
- 保留痕迹:重要的修改(如异常值替换、缺失值填充)应记录在日志中,确保可追溯。
- 分步验证:每完成一步清洗,都需验证结果(如检查填充后是否还有缺失值)。
- 自动化复用:将清洗步骤封装为函数,便于在新数据上重复使用,保证处理逻辑一致。
That’s all, thanks for reading~~
觉得有用就点个赞
、收进收藏
夹吧!关注
我,获取更多干货~