☰
Pandas数据清洗实战:标准化与去重技巧
2026/10/14 12:24:09 网站建设 项目流程

1. 数据清洗的核心价值与Pandas优势

数据清洗是数据分析流程中最为关键的预处理环节,据统计在真实业务场景中,数据科学家80%的时间都消耗在数据清洗上。而Pandas作为Python生态中最强大的数据处理库,其DataFrame结构天然适合处理表格型数据,特别是在处理缺失值、格式转换和重复记录方面具有显著优势。

我最近在为某电商平台分析用户行为数据时,原始数据集存在大量问题:同一用户的注册时间格式不统一(有的用"2023-01-01",有的用"01/01/2023"),商品价格字段混入了文本备注,还有约15%的重复下单记录。通过Pandas的标准化和去重操作,最终将数据可用性从原始的62%提升到了98%。

2. 数据标准化全流程实战

2.1 日期时间标准化

日期字段的混乱是最常见的问题之一。假设我们有包含注册时间的用户数据:

import pandas as pd df = pd.DataFrame({ 'user_id': [101, 102, 103], 'signup_date': ['2023-01-01', '01/15/2023', '2023年2月20日'] })

标准化方案:

# 方法1:统一转datetime类型 df['signup_date'] = pd.to_datetime(df['signup_date'], errors='coerce') # 方法2:自定义格式转换(处理中文日期) def convert_date(date_str): try: return pd.to_datetime(date_str) except: if '年' in date_str: return pd.to_datetime(date_str.replace('年','-').replace('月','-').replace('日','')) return pd.NaT df['signup_date'] = df['signup_date'].apply(convert_date)

注意:errors='coerce'参数会将转换失败的设为NaT(Not a Time),避免程序中断。建议转换后立即检查缺失值:df[df['signup_date'].isna()]

2.2 文本型数字清洗

商品价格字段经常混入非数字字符:

price_data = pd.Series(['¥199', '198元', '200', '特价150'])

清洗步骤:

  1. 移除货币符号和中文单位
  2. 提取数字部分
  3. 转换数字类型
# 正则表达式提取数字 price_data = price_data.str.extract(r'(\d+)', expand=False).astype(float) # 验证结果 print(price_data) # 输出:0 199.0 # 1 198.0 # 2 200.0 # 3 150.0

2.3 分类数据编码

对于有限取值的分类变量(如性别、产品类型),建议使用category类型节省内存:

df['gender'] = ['男', '女', '男', '未知', '女'] df['gender'] = df['gender'].astype('category') print(df['gender'].cat.categories) # 查看所有唯一值

3. 高级去重技术与实战

3.1 基于关键字段的去重

考虑订单数据可能存在完全重复或部分字段重复:

orders = pd.DataFrame({ 'order_id': [1001, 1002, 1003, 1001, 1004], 'user_id': [201, 202, 203, 201, 204], 'amount': [150, 200, 300, 150, 400] })

场景1:保留首次出现的完全重复行

orders.drop_duplicates(inplace=True)

场景2:基于特定列组合去重(如相同user_id和amount视为重复)

orders.drop_duplicates(subset=['user_id', 'amount'], keep='last')

3.2 模糊去重技术

当数据存在轻微差异时(如地址中的"北京市"和"北京"),需要模糊匹配:

from fuzzywuzzy import fuzz addresses = pd.Series(['北京市海淀区', '北京海淀区', '上海浦东新区', '上海市浦东']) # 构建相似度矩阵 similarity_matrix = [[fuzz.ratio(a,b) for b in addresses] for a in addresses] # 设置阈值进行聚类(这里简化为示例) threshold = 80 duplicates = [(i,j) for i in range(len(addresses)) for j in range(i+1, len(addresses)) if similarity_matrix[i][j] > threshold]

3.3 时间窗口去重

对于时间序列数据,可能需要按时间窗口去重。例如识别1小时内重复的操作:

logs = pd.DataFrame({ 'user_id': [1,1,2,1,3], 'action_time': pd.to_datetime([ '2023-01-01 10:00', '2023-01-01 10:30', '2023-01-01 11:00', '2023-01-01 10:45', '2023-01-01 12:00' ]), 'action': ['login', 'search', 'login', 'search', 'login'] }) # 标记1小时内相同用户的重复动作 logs['is_duplicate'] = logs.sort_values('action_time').groupby('user_id')['action'].transform( lambda x: x.rolling(window=2).apply(lambda y: y[0]==y[1] if len(y)>1 else 0) )

4. 实战中的避坑指南

4.1 内存优化技巧

处理大型数据集时,常规操作可能导致内存溢出:

  1. 分块处理:
chunk_size = 100000 for chunk in pd.read_csv('large_file.csv', chunksize=chunk_size): process(chunk) # 自定义处理函数
  1. 类型降级:
dtypes = { 'user_id': 'int32', # 默认int64 'price': 'float32', # 默认float64 'category': 'category' } df = pd.read_csv('data.csv', dtype=dtypes)

4.2 常见陷阱与解决方案

问题1:去重后索引不连续

df = df.drop_duplicates().reset_index(drop=True)

问题2:分组操作意外改变原始数据

# 错误做法(会修改原df): df.groupby('group')['value'].fillna(method='ffill') # 正确做法: df['value'] = df.groupby('group')['value'].ffill()

问题3:混合类型的列导致性能下降

# 检查列类型是否一致 df.apply(lambda x: x.map(type).nunique() > 1)

4.3 验证数据质量的自动化检查

建议在清洗流程中加入以下检查点:

def data_quality_report(df): report = { 'missing_values': df.isnull().sum(), 'duplicates': df.duplicated().sum(), 'data_types': df.dtypes, 'unique_counts': df.nunique() } return pd.DataFrame(report) # 执行检查 dq_report = data_quality_report(df) print(dq_report[dq_report['missing_values'] > 0])

5. 完整案例:电商订单清洗实战

假设我们从数据库导出了原始订单数据:

raw_data = pd.DataFrame({ 'order_id': ['A100', 'A101', 'A100', 'A102', 'A103'], 'user_id': ['U001', 'U002', 'U001', 'U003', 'U002'], 'order_date': ['2023-01-01', '01/02/2023', '2023-01-01', '2023-01-03', '2023年1月4日'], 'amount': ['¥100', '150元', '100', '200', '特价180'], 'status': ['已支付', '已支付', '已支付', '未支付', '已退款'] })

完整清洗流程:

  1. 标准化日期字段
raw_data['order_date'] = pd.to_datetime( raw_data['order_date'].str.replace('年','-').str.replace('月','-').str.replace('日',''), errors='coerce' )
  1. 清洗金额字段
raw_data['amount'] = ( raw_data['amount'] .str.replace(r'[^\d.]', '', regex=True) .astype(float) )
  1. 智能去重(保留最新状态的订单)
clean_data = ( raw_data .sort_values('order_date', ascending=False) .drop_duplicates(subset=['order_id', 'user_id', 'amount'], keep='first') .sort_index() )
  1. 最终验证
assert clean_data['order_id'].is_unique, "存在重复订单ID" assert not clean_data['amount'].isnull().any(), "存在无效金额" assert clean_data['order_date'].between('2023-01-01', '2023-12-31').all(), "日期超出范围"

在实际项目中,我会将上述流程封装成可复用的数据清洗管道(Pipeline),并配合单元测试确保每个步骤的可靠性。对于特别复杂的数据质量问题,建议建立数据质量监控看板,定期跟踪关键指标的改善情况。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询