当前位置:

首页 > 编程开发 > Pandas数据清洗的8大核心技巧完整指南

Pandas数据清洗的8大核心技巧完整指南

本文目录

    数据清洗通常占数据分析工作量的60%-80%。该指南系统整理了Pandas数据清洗的8个核心技巧,包括缺失值、重复值、异常值处理及数据类型转换等,每个技巧配有完整代码示例,适合初中级分析师参考。

    数据清洗在数据分析工作中占据的比例,比大多数人想象的要大得多——通常要占到整个工作量的60%到80%。这意味着,如果你能把这部分做得又快又好,整个分析效率都会上一个台阶。下面这份指南系统地整理了Pandas数据清洗的8个核心技巧,每个技巧都配有完整的代码示例和注释,特别适合初中级数据分析师在日常工作中参考。

    Pandas数据清洗的8大核心技巧完整指南

    一、环境准备

    # ============================================================
    # pandas数据清洗完整指南 - 环境准备
    # ============================================================
    import pandas as pd
    import numpy as np
    # 设置pandas显示选项,方便查看数据
    pd.set_option('display.max_columns', None)   # 显示所有列
    pd.set_option('display.max_rows', 50)        # 最多显示50行
    pd.set_option('display.float_format', lambda x: '%.2f' % x)  # 浮点数保留2位小数
    print("pandas版本:", pd.__version__)
    print("numpy版本:", np.__version__)

    二、构造测试数据集

    我们先构造一个包含各种“脏数据”的真实数据集,模拟电商用户行为数据:

    # ============================================================
    # 构造脏数据集 —— 模拟电商用户行为数据
    # 特意制造各种数据质量问题,便于演示清洗方法
    # ============================================================
    data = {
        'user_id':    [1001, 1002, 1003, 1004, 1005, 1002, 1007, 1008, 1009, 1010],
        'username':   ['张三', '李四', ' 王五 ', 'ZHAO6', '田七', '李四', None, '周九', '吴十', ''],
        'age':        [25, 200, 28, -1, 32, 200, 29, None, 31, 26],
        'gender':     ['男', '女', '男', '未知', '女', '女', '男', '男', '未知', '女'],
        'city':       ['北京', '上海', '广州', '深圳', '杭州', '上海', '成都', None, '武汉', '南京'],
        'purchase_amt': [288.5, 1500.0, 66.0, 999.0, 0, 1500.0, 350.0, 88.0, None, 122.5],
        'order_date': ['2024-01-15', '2024-01-16', '2024/01/17', '20240118', '2024-01-19',
                       '2024-01-16', '2024-01-21', '2024-01-22', '2024-01-23', 'invalid_date'],
        'category':   ['电子', '服装', '食品', '电子', '美妆', '服装', '电子', '食品', '服装', '家居'],
        'score':      [4.5, 3.8, 5.0, 2.1, 4.2, 3.8, None, 4.0, 3.5, 4.8],
    }
    df = pd.DataFrame(data)
    print("原始数据形状:", df.shape)
    print("\n原始数据:")
    print(df)
    print("\n数据类型:")
    print(df.dtypes)

    输出结果(数据概览):

    原始数据形状: (10, 9)

    user_id username age gender city purchase_amt order_date category score

    0 1001 张三 25 男 北京 288.50 2024-01-15 电子 4.5

    1 1002 李四 200 女 上海 1500.00 2024-01-16 服装 3.8

    2 1003 王五 28 男 广州 66.00 2024/01/17 食品 5.0

    3 1004 ZHAO6 -1 未知 深圳 999.00 20240118 电子 2.1

    ...

    三、核心技巧1:缺失值处理

    缺失值是最常见的数据质量问题,处理方式根据业务场景不同而异。

    # ============================================================
    # 技巧1:缺失值检测与处理
    # ============================================================
    # --- 1.1 检测缺失值 ---
    # 查看每列缺失数量和比例
    missing_info = pd.DataFrame({
        '缺失数量': df.isnull().sum(),
        '缺失比例': (df.isnull().sum() / len(df) * 100).round(2),
        '非空数量': df.notnull().sum()
    })
    print("缺失值统计:")
    print(missing_info[missing_info['缺失数量'] > 0])  # 只显示有缺失的列
    # 快速查看:哪些行有缺失值
    rows_with_na = df[df.isnull().any(axis=1)]
    print(f"\n含缺失值的行数:{len(rows_with_na)}")
    # --- 1.2 删除缺失值 ---
    # 删除缺失值比例超过50%的行(可调整阈值)
    df_clean = df.dropna(thresh=int(len(df.columns) * 0.5))
    print(f"\n删除高缺失行后剩余:{len(df_clean)} 行")
    # 只删除关键字段为空的行(user_id不能为空)
    df_clean = df.dropna(subset=['user_id', 'username'])
    print(f"删除user_id/username为空后剩余:{len(df_clean)} 行")
    # --- 1.3 填充缺失值 ---
    df_filled = df.copy()
    # 数值型:用中位数填充(比均值更稳健,不受极端值影响)
    df_filled['age'].fillna(df_filled['age'].median(), inplace=True)
    df_filled['score'].fillna(df_filled['score'].median(), inplace=True)
    df_filled['purchase_amt'].fillna(df_filled['purchase_amt'].median(), inplace=True)
    # 分类型:用众数填充
    df_filled['gender'].fillna(df_filled['gender'].mode()[0], inplace=True)
    # 字符串型:用固定值填充
    df_filled['city'].fillna('未知城市', inplace=True)
    df_filled['username'].fillna('匿名用户', inplace=True)
    # 用前一行的值填充(适合时间序列)
    # df_filled['purchase_amt'].fillna(method='ffill', inplace=True)
    print("\n填充后缺失值数量:")
    print(df_filled.isnull().sum())

    四、核心技巧2:重复值处理

    # ============================================================
    # 技巧2:重复值检测与去重
    # 注意:去重要根据业务逻辑判断"什么是真正的重复"
    # ============================================================
    # --- 2.1 检测重复行 ---
    # 完全重复(所有列都相同)
    full_dup = df.duplicated()
    print(f"完全重复行数:{full_dup.sum()}")
    # 关键字段重复(业务上同一用户的重复订单)
    key_dup = df.duplicated(subset=['user_id', 'order_date'])
    print(f"user_id + order_date 重复行数:{key_dup.sum()}")
    # 查看重复的具体内容
    print("\n重复数据明细:")
    print(df[df.duplicated(subset=['user_id'], keep=False)])  # keep=False 显示所有重复行
    # --- 2.2 去重 ---
    # 保留第一次出现的记录
    df_dedup = df.drop_duplicates(subset=['user_id'], keep='first')
    print(f"\n按user_id去重后:{len(df_dedup)} 行(原始:{len(df)} 行)")
    # 保留最新的记录(先排序再取最后一条)
    df_sorted = df.sort_values('order_date', ascending=True)
    df_dedup_latest = df_sorted.drop_duplicates(subset=['user_id'], keep='last')
    print(f"保留最新记录去重后:{len(df_dedup_latest)} 行")
    # 重置索引(去重后索引可能不连续)
    df_dedup = df_dedup.reset_index(drop=True)
    print("\n去重并重置索引后:")
    print(df_dedup[['user_id', 'username', 'order_date']].head())

    五、核心技巧3:异常值处理

    # ============================================================
    # 技巧3:异常值检测与处理
    # 两种主流方法:IQR箱线图法 + 3σ规则
    # ============================================================
    
    # --- 3.1 业务规则检查(最直接的方法)---
    
    print("=== 业务规则异常检测 ===")
    
    # 年龄异常:人类年龄合理范围 0-120岁
    age_anomaly = df[(df['age']  120)]
    print(f"年龄异常(120)的行数:{len(age_anomaly)}")
    print(age_anomaly[['user_id', 'username', 'age']])
    
    # 消费金额异常:不能为负数
    amt_anomaly = df[df['purchase_amt']  upper)]
        print(f"  Q1={Q1:.2f}, Q3={Q3:.2f}, IQR={IQR:.2f}")
        print(f"  正常范围:[{lower:.2f}, {upper:.2f}]")
        print(f"  异常值数量:{len(outliers)},索引:{outliers.index.tolist()}")
        return lower, upper
    
    # 对年龄列做IQR检测
    print("年龄列IQR检测:")
    age_valid = df[df['age'] > 0]['age']  # 先过滤负值
    lower_age, upper_age = detect_outliers_iqr(age_valid)
    
    # --- 3.3 异常值处理策略 ---
    
    df_clean2 = df.copy()
    
    # 策略1:直接删除异常行
    df_clean2 = df_clean2[(df_clean2['age'] >= 0) & (df_clean2['age']  120, 'age'] = median_age
    
    print(f"\n处理异常值后数据量:{len(df_clean2)} 行")
    
    

    六、核心技巧4:数据类型转换

    # ============================================================
    # 技巧4:数据类型检查与转换
    # 数据类型不对是很多报错的根源
    # ============================================================
    
    df_typed = df_filled.copy()
    
    print("转换前数据类型:")
    print(df_typed.dtypes)
    
    # --- 4.1 日期类型转换 ---
    
    # pd.to_datetime 支持多种格式,errors='coerce' 把无法解析的转为 NaT
    df_typed['order_date'] = pd.to_datetime(df_typed['order_date'], errors='coerce')
    print(f"\n日期转换后,NaT数量:{df_typed['order_date'].isnull().sum()}")
    
    # 从日期中提取更多特征
    df_typed['order_year']  = df_typed['order_date'].dt.year
    df_typed['order_month'] = df_typed['order_date'].dt.month
    df_typed['order_day']   = df_typed['order_date'].dt.day
    df_typed['order_weekday'] = df_typed['order_date'].dt.dayofweek  # 0=周一, 6=周日
    
    # --- 4.2 数值类型转换 ---
    
    # 字符串转数值,无法转换的设为 NaN
    df_typed['age'] = pd.to_numeric(df_typed['age'], errors='coerce')
    df_typed['purchase_amt'] = pd.to_numeric(df_typed['purchase_amt'], errors='coerce')
    
    # --- 4.3 分类类型转换(节省内存)---
    
    # 低基数列(取值有限)转为 category 类型,大幅节省内存
    cat_cols = ['gender', 'city', 'category']
    for col in cat_cols:
        df_typed[col] = df_typed[col].astype('category')
    
    print("\n转换后数据类型:")
    print(df_typed.dtypes)
    
    # 内存对比
    print(f"\n原始内存:{df.memory_usage(deep=True).sum() / 1024:.1f} KB")
    print(f"转换后内存:{df_typed.memory_usage(deep=True).sum() / 1024:.1f} KB")
    
    

    七、核心技巧5:字符串清洗

    # ============================================================
    # 技巧5:字符串清洗(str accessor 系列方法)
    # 处理姓名/地址等文本字段的常见问题
    # ============================================================
    
    df_str = df_filled.copy()
    
    # --- 5.1 去除空格 ---
    
    # strip() 去首尾空格,lstrip() 去左侧,rstrip() 去右侧
    df_str['username'] = df_str['username'].str.strip()
    
    # 去除中间多余空格(正则替换)
    df_str['username'] = df_str['username'].str.replace(r'\s+', '', regex=True)
    
    # --- 5.2 大小写统一 ---
    
    # 统一转为大写(适合身份证、订单号等编码)
    df_str['username'] = df_str['username'].str.upper()
    
    # 或首字母大写
    # df_str['username'] = df_str['username'].str.title()
    
    # --- 5.3 字符串过滤与筛选 ---
    
    # 筛选包含特定字符的行
    china_users = df_str[df_str['city'].str.contains('京|沪|穗', na=False)]
    print(f"一线城市用户数:{len(china_users)}")
    
    # 过滤掉空字符串(空字符串不是NaN,要单独处理)
    df_str = df_str[df_str['username'].str.strip() != '']
    df_str = df_str[df_str['username'].notna()]  # 再过滤NaN
    
    # --- 5.4 字符串提取 ---
    
    # 从字符串中提取数字
    sample = pd.Series(['订单001', '订单002abc', '无编号', '订单100'])
    order_nums = sample.str.extract(r'(\d+)', expand=False)  # 提取数字部分
    print("\n订单编号提取:")
    print(order_nums)
    
    # --- 5.5 字符串替换 ---
    
    # 替换特定字符
    df_str['gender'] = df_str['gender'].str.replace('未知', '保密')
    
    print("\n字符串清洗完成,username示例:")
    print(df_str['username'].head(8))
    
    

    八、核心技巧6:数据标准化与归一化

    # ============================================================
    # 技巧6:数值标准化(机器学习前必做的预处理步骤)
    # Min-Max归一化 vs Z-score标准化
    # ============================================================
    
    df_norm = df_filled.copy()
    
    # 只处理数值列
    numeric_cols = ['age', 'purchase_amt', 'score']
    
    # --- 6.1 Min-Max 归一化(将数值缩放到[0,1]区间)---
    # 适用场景:神经网络、KNN、聚类等对量纲敏感的算法
    
    def minmax_normalize(series):
        """Min-Max归一化公式:(x - min) / (max - min)"""
        return (series - series.min()) / (series.max() - series.min())
    
    for col in numeric_cols:
        df_norm[f'{col}_minmax'] = minmax_normalize(df_norm[col])
    
    # --- 6.2 Z-score 标准化(均值为0,标准差为1)---
    # 适用场景:线性回归、SVM、PCA等假设正态分布的算法
    
    def zscore_normalize(series):
        """Z-score公式:(x - mean) / std"""
        return (series - series.mean()) / series.std()
    
    for col in numeric_cols:
        df_norm[f'{col}_zscore'] = zscore_normalize(df_norm[col])
    
    print("归一化和标准化结果对比(前5行):")
    compare_cols = ['purchase_amt', 'purchase_amt_minmax', 'purchase_amt_zscore']
    print(df_norm[compare_cols].head())
    
    print("\n归一化后统计:")
    print(df_norm[['purchase_amt_minmax', 'purchase_amt_zscore']].describe().round(3))
    
    

    九、核心技巧7:分组聚合发现数据问题

    # ============================================================
    # 技巧7:用分组聚合快速发现数据质量问题
    # 这是数据分析师常用的"数据探查"手段
    # ============================================================
    
    df_check = df_filled.copy()
    
    # --- 7.1 按城市分组,查看各城市数据分布 ---
    
    city_stats = df_check.groupby('city').agg(
        用户数=('user_id', 'count'),
        平均年龄=('age', 'mean'),
        平均消费=('purchase_amt', 'mean'),
        最高消费=('purchase_amt', 'max'),
        最低消费=('purchase_amt', 'min'),
    ).round(2)
    
    print("各城市数据统计:")
    print(city_stats)
    
    # --- 7.2 交叉分析:性别 × 品类 的消费习惯 ---
    
    cross_table = df_check.pivot_table(
        values='purchase_amt',
        index='gender',
        columns='category',
        aggfunc='mean',
        fill_value=0  # 缺失组合填0
    ).round(2)
    
    print("\n性别×品类消费交叉表:")
    print(cross_table)
    
    # --- 7.3 用 value_counts 检查类别字段 ---
    
    print("\n性别分布(包含异常值):")
    print(df['gender'].value_counts(dropna=False))  # dropna=False 显示空值统计
    
    print("\n城市分布 Top5:")
    print(df['city'].value_counts().head())
    
    

    十、核心技巧8:构建数据清洗Pipeline

    # ============================================================
    # 技巧8:将所有清洗步骤封装成Pipeline(生产环境推荐)
    # 好处:可复用、可测试、流程透明
    # ============================================================
    
    def clean_user_data(df_raw):
        """
        用户数据清洗完整Pipeline
        
        Parameters:
            df_raw: 原始DataFrame
        
        Returns:
            df_clean: 清洗后的DataFrame
            report: 清洗报告字典
        """
        df = df_raw.copy()
        report = {'原始行数': len(df)}
        
        # Step1: 删除完全重复行
        before = len(df)
        df = df.drop_duplicates(subset=['user_id'], keep='first')
        report['去重删除行数'] = before - len(df)
        
        # Step2: 处理异常值(业务规则)
        df = df[(df['age'].isna()) | (df['age'].between(0, 120))]
        df = df[(df['purchase_amt'].isna()) | (df['purchase_amt'] >= 0)]
        report['异常值删除行数'] = len(df_raw.drop_duplicates(subset=['user_id'])) - len(df)
        
        # Step3: 字符串清洗
        if 'username' in df.columns:
            df['username'] = df['username'].str.strip().replace('', np.nan)
        
        # Step4: 类型转换
        df['order_date'] = pd.to_datetime(df['order_date'], errors='coerce')
        
        # Step5: 缺失值填充
        df['age'].fillna(df['age'].median(), inplace=True)
        df['purchase_amt'].fillna(0, inplace=True)
        df['score'].fillna(df['score'].median(), inplace=True)
        df['city'].fillna('未知', inplace=True)
        df['gender'].fillna('保密', inplace=True)
        
        # Step6: 重置索引
        df = df.reset_index(drop=True)
        
        report['清洗后行数'] = len(df)
        report['清洗率'] = f"{(1 - len(df)/report['原始行数'])*100:.1f}%"
        
        return df, report
    
    # 执行Pipeline
    df_final, clean_report = clean_user_data(df)
    
    print("="*40)
    print("数据清洗报告:")
    print("="*40)
    for key, value in clean_report.items():
        print(f"  {key}: {value}")
    
    print(f"\n最终数据预览:")
    print(df_final.head())
    print(f"\n最终数据形状:{df_final.shape}")
    print(f"\n最终缺失值统计:")
    print(df_final.isnull().sum())
    
    

    十一、数据清洗最佳实践总结

    场景推荐方法注意事项
    缺失值 - 数值型中位数填充比均值更稳健,不受极端值影响
    缺失值 - 分类型众数填充或填"未知",保留信息
    重复值按业务键去重先排序再去重,保留最新/最优记录
    异常值IQR法 + 业务规则异常≠错误,先分析再处理
    日期格式pd.to_datetime errors='coerce'统一为datetime64类型
    字符串str.strip() + 正则空字符串≠NaN,要单独处理
    数值标准化根据算法选Min-Max或Z-score训练集fit,测试集transform
    批量处理封装Pipeline函数记录每步处理量,方便排查

    结语

    数据清洗没有“万能公式”,需要结合具体的业务场景和数据特点来选择最合适的方法。建议每次清洗都输出一份清洗报告,记录每步处理了多少数据,方便后续复盘和维护。

    本文内容来源于网友投稿,如有侵权请联系删除。
    作者最新文章
    编程开发
    相关文章 更多
    PHP递归性能优化技巧与迭代替代方案
    PHP递归性能优化技巧与迭代替代方案

    解析PHP递归函数在树形数据处理中的性能瓶颈,提供预加载数据消除I/O、使用显式栈替代深层递归的实战方案,帮助开发者在代码可读性与执行效率间做出合理取舍。

    Java测试中怎么使用Mockito模拟依赖对象
    Java测试中怎么使用Mockito模拟依赖对象

    详细讲解在Java单元测试中如何使用Mockito模拟依赖对象,包括引入依赖、创建Mock、打桩返回值、行为验证以及Mock与Spy的核心差异和常见陷阱排查。

    链表删除节点的时间复杂度是多少及其详细分析
    链表删除节点的时间复杂度是多少及其详细分析

    详细分析链表删除节点的时间复杂度,深入探讨单链表与双向链表在不同已知前提下的查找与删除开销,并结合完整代码与清晰图解进行对比总结。

    codex如何配置模型参数及文件设置教程
    codex如何配置模型参数及文件设置教程

    想知道如何让AI写出的代码更贴合你的习惯?本文手把手教你在VS Code中调整Codex相关模型参数,通过修改配置文件优化温度值和令牌限制,解决代码建议不准确或响应慢的问题。

    Claude Code AI编程工具实力揭秘与编程助手实测
    Claude Code AI编程工具实力揭秘与编程助手实测

    通过实测展示Claude Code在终端中如何理解自然语言指令、自动修改代码文件并处理复杂编程任务,帮助开发者评估其实际辅助能力。

    winforms教程自学入门与基础开发步骤详解
    winforms教程自学入门与基础开发步骤详解

    本教程详细讲解如何使用Visual Studio创建WinForms项目,通过添加按钮和标签控件并编写点击事件代码,实现一个基础的计数器功能,适合C#初学者快速上手Windows窗体应用开发。

    Cursor自动补全设置教程教你快速开启代码补全功能
    Cursor自动补全设置教程教你快速开启代码补全功能

    详解Cursor编辑器中自动补全功能的开启与优化设置,涵盖Tab触发机制、上下文窗口调整及模型切换,帮助开发者解决补全延迟、干扰大等问题,提升编码流畅度。

    pandas的数据格式怎么转换和设置方法教程
    pandas的数据格式怎么转换和设置方法教程

    详解Pandas中数据格式转换的核心方法,包括astype强制转换、to_numeric容错处理及日期解析技巧,解决常见类型错误并提升数据处理效率。

    VS Code中文设置方法 简体语言包安装与切换教程
    VS Code中文设置方法 简体语言包安装与切换教程

    详细介绍在Visual Studio Code中安装Chinese (Simplified)语言包的方法,包括通过扩展市场搜索、安装及自动重启切换至简体中文界面的完整步骤,帮助开发者快速将编辑器本地化。

    cursor安装过程无法更改安装位置的解决方法
    cursor安装过程无法更改安装位置的解决方法

    针对Cursor安装包默认锁定C盘且无路径选择界面的问题,提供通过手动移动文件并创建目录联结(Symbolic Link)的解决方案,实现将软件安装在其他磁盘分区。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    macOS软件
    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    Mac软件 更多
    photoshop
    photoshop
    Windows、macOS 、 iPad

    Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

    Blender
    Blender
    Windows、macOS 和 Linux

    Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。

    灵活计算器
    灵活计算器
    macOS/iOS/Android

    灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

    WINDOWS 更多
    3dmax(3ds max)
    3dmax(3ds max)
    Windows

    Autodesk 3ds Max 是一款专业的三维建模、动画与渲染软件,广泛应用于建筑可视化、游戏开发、影视动画、广告设计和产品展示等领域。

    photoshop
    photoshop
    Windows、macOS 、 iPad

    Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

    Blender
    Blender
    Windows、macOS 和 Linux

    Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。