数据清洗是数据分析流程中最耗时的环节,往往占据整个项目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)]

五、批量数据转换

面对多列需要相同处理的场景,使用applyapplymap可以避免重复代码。

# 创建示例数据
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%的常见数据问题,核心原则包括:

  1. 优先使用Pandas内置方法,避免手动循环
  2. 缺失值处理需区分数值型和类别型数据
  3. 重复数据要根据业务主键检测
  4. 异常值处理需结合数据分布选择合适方法
  5. 批量处理多列时善用apply家族函数

数据清洗没有万能公式,需要结合业务理解选择合适的策略。建议清洗过程中每一步都保留中间结果,便于回溯和验证。熟练掌握这些技巧,能让你从繁琐的数据整理工作中解放出来,将更多精力投入到有价值的数据分析中。