自学教程 学编程从这里开始

Pandas 数据清洗

数据清洗是数据分析最耗时的环节,核心目标:处理缺失值、重复值、异常值、格式错乱、脏文本、索引问题,得到干净、可直接分析的数据集。
前置:import pandas as pd,示例沿用前面的销售明细数据。

一、准备示例数据

import pandas as pd
import numpy as np

data = {
    "订单号": ["A001", "A002", "A002", "A003", "A004", None, "A005"],
    "商品名称": ["键盘", "鼠标", "鼠标", "显示器", "耳机", "键盘", "显示器"],
    "数量": [2, 1, 1, np.nan, 3, 2, -5],
    "单价": [89, 45, 45, 1200, 199, 89, 1350],
    "下单日期": ["2026-10-01", "2026/10/02", "2026/10/02", "2026-10-03", "2026-10-04", "2026-10-01", "2026-10-05"],
    "客户": ["张三", "李四", "李四", " 王五 ", "赵六", "张三", "孙七"]
}
df = pd.DataFrame(data)
print(df)

二、第一步:探查脏数据(先看再改)

# 基础信息
df.info()
# 描述统计(看数值异常)
df.describe()
# 缺失值统计
df.isnull().sum()
# 重复行检查
df.duplicated().sum()
# 查看唯一值
df["商品名称"].unique()
  • isnull():判断是否为空(NaN / None)
  • duplicated():判断整行是否重复,默认全部列匹配

三、处理缺失值

3种常用策略:删除、填充、标记

# 1. 删除缺失行(any:任意一列空就删;all:全部列空才删)
df_drop = df.dropna(how="any")

# 2. 填充缺失值
df_fill = df.copy()
# 固定值填充
df_fill["订单号"] = df_fill["订单号"].fillna("未知")
# 均值填充(数值列)
df_fill["数量"] = df_fill["数量"].fillna(df_fill["数量"].mean())
# 前向填充 / 后向填充(时序数据常用)
df_fill["数量"] = df_fill["数量"].fillna(method="ffill")

# 3. 增加标记列,记录该位置原本是否缺失(推荐,保留原始信息)
df_fill["数量_is_missing"] = df["数量"].isnull()

注意:fillna(method=...) 在新版 pandas 已经废弃,推荐:
df_fill["数量"] = df_fill["数量"].ffill()

四、处理重复数据

# 查看重复行
df[df.duplicated(keep=False)]

# 删除重复行
# keep="first" 保留第一条;last保留最后一条;False全部删除
df_no_dup = df.drop_duplicates(keep="first")

# 只按指定列去重(比如只按订单号+商品去重)
df_no_dup2 = df.drop_duplicates(subset=["订单号", "商品名称"], keep="first")

五、处理文本脏数据(空格、大小写、乱码)

df_clean = df.copy()
# 去除首尾空格
df_clean["客户"] = df_clean["客户"].str.strip()
# 全部转小写/大写
df_clean["商品名称"] = df_clean["商品名称"].str.lower()
# 替换脏字符
df_clean["商品名称"] = df_clean["商品名称"].str.replace("【键盘】", "键盘", regex=False)
# 判断是否包含指定文本
df_clean[df_clean["商品名称"].str.contains("鼠标", na=False)]

六、处理异常值(离群值、逻辑错误)

示例里数量 -5 属于业务异常(购买数量不能负数)

方式1:布尔筛选过滤

# 业务规则:数量必须 >0
df_valid = df_clean[(df_clean["数量"] > 0) & (df_clean["数量"] <= 100)]

方式2:IQR 四分位法剔除统计离群值

def remove_outlier(df, col):
    Q1 = df[col].quantile(0.25)
    Q3 = df[col].quantile(0.75)
    IQR = Q3 - Q1
    lower = Q1 - 1.5 * IQR
    upper = Q3 + 1.5 * IQR
    return df[(df[col] >= lower) & (df[col] <= upper)]

df_valid = remove_outlier(df_clean, "单价")

提示:异常值不一定直接删掉!业务场景优先:标记、单独抽离、或者修正,不要盲目删数据。

七、日期格式统一转换

原始数据有 2026/10/02 和 2026-10-01 混杂

df_clean["下单日期"] = pd.to_datetime(df_clean["下单日期"])
# 提取年、月、日
df_clean["year"] = df_clean["下单日期"].dt.year
df_clean["month"] = df_clean["下单日期"].dt.month
df_clean["day"] = df_clean["下单日期"].dt.day

pd.to_datetime 自动兼容多种分隔符,遇到无法解析的脏日期:

# 无法解析的变成 NaT
df_clean["下单日期"] = pd.to_datetime(df_clean["下单日期"], errors="coerce")

八、数据类型修正

常见坑:数字被识别成字符串、金额带¥符号

# 去除符号再转数字
df_clean["单价"] = df_clean["单价"].astype(str).str.replace("¥", "", regex=False)
df_clean["单价"] = pd.to_numeric(df_clean["单价"], errors="coerce")

九、替换映射、分类修正

# 批量替换
df_clean["商品名称"] = df_clean["商品名称"].replace({"keyboard": "键盘", "mouse": "鼠标"})

十、完整清洗流水线(可直接复用)

def clean_sales_df(raw_df):
    df = raw_df.copy()
    # 1. 文本去空格
    df["客户"] = df["客户"].str.strip()
    # 2. 日期转换
    df["下单日期"] = pd.to_datetime(df["下单日期"], errors="coerce")
    # 3. 去重
    df = df.drop_duplicates(subset=["订单号","商品名称"], keep="first")
    # 4. 业务异常过滤
    df = df[(df["数量"] > 0) & (df["单价"] > 0)]
    # 5. 缺失值标记
    df["数量缺失"] = df["数量"].isnull()
    df["订单号"] = df["订单号"].fillna("未知订单")
    return df

df_result = clean_sales_df(df)
print(df_result)

十一、常见坑汇总

  1. NaN 和 空字符串 "" 不是一回事,isnull() 识别不到空字符串,需要额外处理:df["col"] == ""
  2. 原地修改 inplace=True 不推荐,容易引发链式赋值警告,优先 df = df.xxx()
  3. 清洗前永远保留原始副本:df_raw = df.copy()
  4. 日期清洗一定要加 errors="coerce",防止脏数据直接抛异常中断脚本
  5. 不要无脑删除缺失值:缺失本身可能代表业务含义(用户未填写)
标签:

0 条笔记