如何用Python实现Excel数据的清洗

30 阅读7分钟

你想掌握的是用Python完成Excel数据清洗的核心方法,解决手动处理Excel中脏数据(空值、重复值、格式错误、异常值等)效率低、易出错的问题。本文以pandas(核心数据处理库)为核心,结合实战案例讲解Excel数据清洗的全流程,覆盖最常见的脏数据类型及对应的自动化处理方案,最终输出干净、可用的数据集。

一、核心工具准备

数据清洗的核心库是pandas,搭配openpyxl实现.xlsx文件的读写,先完成环境安装:

# 安装核心库
pip install pandas openpyxl
  • pandas:提供一站式数据清洗API,支持空值、重复值、格式转换、异常值处理等;
  • openpyxl:作为pandas读写.xlsx文件的引擎,无需单独调用,安装即可。

二、Excel数据清洗完整流程(实战案例)

以“电商销售数据.xlsx”为例(包含字段:订单号、日期、商品类别、销售额、购买数量、客户地区),先读取数据,再按“识别脏数据→处理脏数据→验证清洗结果”的逻辑完成清洗。

步骤1:读取Excel数据并初步探查

先加载数据,通过基础方法识别脏数据类型(空值、重复值、数据类型异常等):

import pandas as pd

# 读取Excel数据(指定工作表,跳过空行)
df = pd.read_excel(
    "电商销售数据.xlsx",
    sheet_name="销售明细",
    skiprows=1  # 跳过首行标题外的空行
)

# 1. 查看数据基本信息(行数、列数、数据类型、非空值数量)
print("数据基本信息:")
print(df.info())

# 2. 查看前5行数据,直观识别异常
print("\n数据前5行:")
print(df.head())

# 3. 统计空值数量(重点关注关键字段)
print("\n各字段空值数量:")
print(df.isnull().sum())

# 4. 查看重复行数量
print("\n重复行数量:", df.duplicated().sum())

# 5. 数值字段的统计摘要(识别异常值,如销售额为负)
print("\n数值字段统计摘要:")
print(df[["销售额", "购买数量"]].describe())

步骤2:处理空值(缺失值)

空值是最常见的脏数据,需根据字段重要性选择“删除”或“填充”策略,避免手动逐个单元格补全:

# 策略1:删除关键字段(订单号、销售额)为空的行(无法补全的核心数据)
df = df.dropna(subset=["订单号", "销售额"])

# 策略2:填充非关键字段的空值(如客户地区、商品类别)
# 客户地区空值填充为“未知地区”(分类字段)
df["客户地区"] = df["客户地区"].fillna("未知地区")
# 商品类别空值填充为出现次数最多的类别(众数填充)
df["商品类别"] = df["商品类别"].fillna(df["商品类别"].mode()[0])

# 验证空值处理结果
print("\n空值处理后各字段空值数量:")
print(df.isnull().sum())

步骤3:处理重复值

重复行(如同一订单号多次出现)会导致统计结果错误,需批量删除:

# 按“订单号”去重(订单号唯一,重复即为无效数据)
# keep="first"保留第一次出现的行,删除后续重复行
df = df.drop_duplicates(subset=["订单号"], keep="first")

# 验证重复值处理结果
print("\n去重后数据行数:", len(df))
print("重复行数量:", df.duplicated().sum())

步骤4:数据类型转换

Excel中常出现“日期存为文本”“销售额存为字符串”等格式错误,需统一转换为正确类型:

# 1. 日期字段转换(将文本格式的日期转为datetime类型)
# errors="coerce"将无法转换的日期转为NaT,后续可删除
df["日期"] = pd.to_datetime(df["日期"], errors="coerce")
# 删除日期转换失败的行(无效日期)
df = df.dropna(subset=["日期"])

# 2. 数值字段转换(将字符串格式的销售额转为浮点数)
# 先替换销售额中的非数字字符(如“元”“,”),再转换
df["销售额"] = df["销售额"].astype(str).str.replace("元", "").str.replace(",", "")
df["销售额"] = pd.to_numeric(df["销售额"], errors="coerce")

# 3. 分类字段标准化(如客户地区统一为“XX省”格式)
# 将“北京”“北京市”统一为“北京市”
df["客户地区"] = df["客户地区"].replace({"北京": "北京市", "上海": "上海市"})

# 验证数据类型转换结果
print("\n数据类型转换后信息:")
print(df.dtypes)

步骤5:处理异常值

异常值(如销售额为负、购买数量过大)会影响分析结果,需识别并修正/删除:

# 1. 过滤数值异常值(销售额≥0、购买数量≥1)
df = df[(df["销售额"] >= 0) & (df["购买数量"] >= 1)]

# 2. 处理极端值(如购买数量超过100的异常订单,视为录入错误)
# 方法1:删除极端值(适合数据量较大的场景)
df = df[df["购买数量"] <= 100]
# 方法2:替换为合理值(如中位数,适合数据量小的场景)
# df.loc[df["购买数量"] > 100, "购买数量"] = df["购买数量"].median()

# 验证异常值处理结果
print("\n异常值处理后数值字段统计:")
print(df[["销售额", "购买数量"]].describe())

步骤6:字段标准化与衍生列(可选)

根据分析需求统一字段格式,或新增衍生列,替代Excel中的手动分列、公式计算:

# 1. 商品类别标准化(统一为小写,去空格)
df["商品类别"] = df["商品类别"].str.lower().str.strip()

# 2. 新增衍生列:单客单价(销售额/购买数量)
df["单客单价"] = df["销售额"] / df["购买数量"]
# 保留2位小数(避免多位小数混乱)
df["单客单价"] = df["单客单价"].round(2)

# 3. 按日期提取“月份”“季度”,方便后续分析
df["月份"] = df["日期"].dt.month
df["季度"] = df["日期"].dt.quarter

print("\n标准化后数据前5行:")
print(df.head())

步骤7:验证清洗结果并导出

清洗完成后,验证数据质量,再导出为干净的Excel文件,替代手动复制粘贴:

# 最终验证:无空值、无重复值、无异常值
print("最终清洗结果验证:")
print("空值数量:", df.isnull().sum().sum())
print("重复行数量:", df.duplicated().sum())
print("销售额异常值数量:", len(df[df["销售额"] < 0]))
print("最终数据行数:", len(df))

# 导出清洗后的数据到新Excel
df.to_excel(
    "电商销售数据_清洗后.xlsx",
    sheet_name="清洗后明细",
    index=False,  # 不导出索引列
    engine="openpyxl"
)
print("\n清洗后的数据已导出到Excel!")

三、高频数据清洗场景拓展

场景1:批量清洗多份Excel文件

若有多个Excel文件(如各月份销售数据),可遍历文件夹批量清洗:

import os

# 定义文件夹路径
folder_path = "各月份销售数据"
# 存储所有清洗后的数据
cleaned_data = []

# 遍历文件夹中的Excel文件
for file_name in os.listdir(folder_path):
    if file_name.endswith(".xlsx"):
        file_path = os.path.join(folder_path, file_name)
        # 读取单个文件
        df_temp = pd.read_excel(file_path, sheet_name="销售明细")
        
        # 执行相同的清洗逻辑(空值、重复值、异常值处理)
        df_temp = df_temp.dropna(subset=["订单号", "销售额"])
        df_temp = df_temp.drop_duplicates(subset=["订单号"])
        df_temp["销售额"] = pd.to_numeric(df_temp["销售额"], errors="coerce")
        df_temp = df_temp[df_temp["销售额"] >= 0]
        
        # 标记数据来源月份
        df_temp["数据月份"] = file_name.replace(".xlsx", "")
        cleaned_data.append(df_temp)

# 合并所有清洗后的数据
df_all = pd.concat(cleaned_data, ignore_index=True)
# 导出合并后的结果
df_all.to_excel("各月份销售数据_合并清洗后.xlsx", index=False, engine="openpyxl")
print("多文件批量清洗完成!")

场景2:处理Excel中的合并单元格

Excel合并单元格会导致读取后出现大量空值,需先还原合并单元格再清洗:

from openpyxl import load_workbook

# 步骤1:用openpyxl读取Excel,还原合并单元格的值
wb = load_workbook("带合并单元格的数据.xlsx")
ws = wb.active

# 获取所有合并单元格的范围
merged_ranges = ws.merged_cells.ranges
for merged_range in merged_ranges:
    # 获取合并单元格的左上角值
    value = ws[merged_range.start_cell.coordinate].value
    # 遍历合并范围,填充值
    for row in ws[merged_range]:
        for cell in row:
            cell.value = value

# 步骤2:保存还原后的数据,再用pandas读取清洗
wb.save("还原合并单元格后.xlsx")
wb.close()

# 步骤3:pandas读取并继续常规清洗
df = pd.read_excel("还原合并单元格后.xlsx")
df = df.dropna(subset=["订单号"])
print("合并单元格处理完成!")

四、常见问题与解决方法

  1. 日期转换失败:Excel中日期格式不统一(如“2025/01/01”“2025-01-01”“01-01-2025”),可先用str.replace统一分隔符,再转换:
    df["日期"] = df["日期"].str.replace("-", "/").str.replace(".", "/")
    df["日期"] = pd.to_datetime(df["日期"], errors="coerce")
    
  2. 销售额含特殊字符:如“1000元”“1,200”,需先清理非数字字符再转换:
    df["销售额"] = df["销售额"].astype(str).str.replace(r"[^\d.]", "", regex=True)
    
  3. 大数据量清洗卡顿:读取时仅加载需要的列(usecols参数),减少内存占用:
    df = pd.read_excel("大数据量.xlsx", usecols=["订单号", "日期", "销售额"])
    
  4. 分类字段取值混乱:如“电子产品”“电子类”“电子”,用replace统一:
    df["商品类别"] = df["商品类别"].replace({"电子类": "电子产品", "电子": "电子产品"})