Skip to content

Python 数据处理总览

Python 做数据处理,最常用的组合是 NumPy 和 Pandas。NumPy 负责高效数组计算,Pandas 负责表格数据读取、清洗、分析和导出。

零基础先记住一句话:

数据处理不是“会写 Pandas API”,而是把来源不稳定、格式不统一、质量不可信的原始数据,变成可统计、可追溯、可重复执行的可信数据。

如果只会 read_csv()groupby(),但不知道字段类型、缺失值、重复数据、异常值、编码、业务主键和口径校验,最后很容易算出一份“看起来很精确、实际上完全错误”的报表。

学习目标

学完本页,你应该能回答:

  1. NumPy 和 Pandas 分别解决什么问题。
  2. 数据处理为什么必须先检查数据质量。
  3. 一份 CSV/Excel 从读取到导出完整经历哪些步骤。
  4. 缺失值、重复值、异常值、类型错误分别怎么处理。
  5. 为什么金额、日期、业务状态不能直接相信原始字段。
  6. 为什么脚本必须可重复、可审计,而不是在 Excel 里手动改。
  7. 能写一个可运行的商业数据清洗 Demo。
  8. 面试时能说清 Pandas 数据处理流程、常见坑和排查方法。

NumPy 和 Pandas 怎么分工

mermaid
flowchart TD
    A["原始业务数据"] --> B["Pandas 读取 CSV/Excel/SQL"]
    B --> C["DataFrame 表格清洗"]
    C --> D["Series 列计算"]
    D --> E["NumPy 数组和向量化"]
    C --> F["分组、聚合、透视、导出"]
工具主要对象适合做什么不适合做什么
NumPyndarray大量数值计算、矩阵、向量化、科学计算直接处理复杂业务表格和字段语义
PandasDataFrame / SeriesCSV/Excel/SQL 表格清洗、聚合、透视、导出超大数据量分布式计算
原生 Pythonlist / dict / set小规模逻辑、校验、业务规则、脚本控制流大规模列式计算

可以这样理解:

  1. NumPy 更靠近“计算引擎”。
  2. Pandas 更靠近“业务表格处理”。
  3. 商业数据清洗通常以 Pandas 为主,必要时借助 NumPy 做数值计算。

数据处理为什么不是简单 API 调用

假设你拿到一份订单 CSV:

text
order_id,city,amount,status,created_at
1001,北京,99.5,paid,2026-07-01
1002,上海,120元,paid,2026/07/01
1002,上海,120,paid,2026/07/01
1003,北京,,refund,unknown
1004,深圳,-80,paid,2026-07-02

直接求和会有很多问题:

问题如果不处理会怎样
120元 是字符串金额列无法正确求和,或被当成 object
order_id=1002 重复销售额重复计算
amount 为空直接填 0 可能掩盖脏数据
created_at=unknown日期聚合失败
amount=-80订单金额为负可能是异常,也可能是退款口径
refundpaid 混在一起不区分状态会把退款算进销售

所以数据处理要先问业务问题:

  1. 这份数据的主键是什么。
  2. 哪些字段必填。
  3. 哪些状态应该参与统计。
  4. 金额为空代表 0,还是代表数据缺失。
  5. 负数金额是异常还是业务含义。
  6. 重复订单保留第一条、最后一条,还是按更新时间判断。

标准处理流程

mermaid
flowchart TD
    A["拿到原始数据"] --> B["确认字段含义和业务口径"]
    B --> C["读取文件并指定编码"]
    C --> D["查看 shape、head、info"]
    D --> E["字段类型转换"]
    E --> F["缺失值分析"]
    F --> G["重复值处理"]
    G --> H["异常值处理"]
    H --> I["业务规则过滤"]
    I --> J["分组聚合和校验"]
    J --> K["导出结果"]
    K --> L["保存脚本、日志和处理报告"]

每一步都不是形式主义。

步骤为什么要做不做会怎样
字段口径确认知道每列代表什么技术上算对,业务上算错
指定编码防止中文乱码城市、姓名、机构名乱码
查看结构确认行数、列数、样例不知道读入是否完整
类型转换数字和日期才能正确计算字符串求和、日期排序错误
缺失分析判断缺失是正常还是脏数据无脑填充导致结果失真
去重防止重复计算金额、数量、人数被放大
异常处理排除或标记明显不合理数据极端值拉歪统计结果
业务过滤只统计符合口径的数据把退款、取消、测试数据算进去
校验对比总数、金额、样例错误进入报表还不知道
保存脚本可复现、可审计下次数据来了只能手工重做

第一步:读取前先确认数据来源

商业数据通常来自:

来源常见问题
CSV编码、分隔符、引号、换行、字段类型
Excel多 sheet、合并单元格、表头不在第一行
数据库SQL 口径、分页、时间范围、权限
API超时、分页、重复拉取、字段缺失
日志格式不稳定、字段嵌套、时间格式多样

读取前至少确认:

  1. 文件是否完整。
  2. 表头在哪一行。
  3. 编码是 utf-8utf-8-sig 还是 gbk
  4. 日期和金额字段格式。
  5. 是否包含测试数据。
  6. 是否有业务主键。

第二步:读取并做结构检查

python
import pandas as pd


df = pd.read_csv("orders_raw.csv", encoding="utf-8")

print("行列数:", df.shape)
print("前 5 行:")
print(df.head())
print("字段类型和非空数量:")
print(df.info())

shapehead()info() 是初学者必须养成的习惯。

方法看什么例子
df.shape行数和列数是否少读、多读
df.head()样例数据表头是否正确、字段是否错位
df.info()类型和非空数量金额是否 object、日期是否 object
df.describe()数值分布最大值、最小值是否异常
df.isna().sum()缺失数量哪些字段缺失严重

如果读取后发现所有列都挤在一列,可能是分隔符不对:

python
df = pd.read_csv("orders_raw.csv", sep=";", encoding="utf-8")

如果中文乱码,尝试:

python
df = pd.read_csv("orders_raw.csv", encoding="gbk")

第三步:字段类型转换

Pandas 读取 CSV 时不一定能猜对类型。金额、日期、ID、手机号尤其要谨慎。

金额转换

python
df["amount"] = (
    df["amount"]
    .astype(str)
    .str.replace("元", "", regex=False)
    .str.strip()
)
df["amount"] = pd.to_numeric(df["amount"], errors="coerce")

errors="coerce" 表示转换失败变成缺失值。这样不会让整个脚本直接崩,但后面必须检查转换失败的数据。

python
bad_amount = df[df["amount"].isna()]
print("金额无法转换的行数:", len(bad_amount))
print(bad_amount[["order_id", "amount"]].head())

日期转换

python
df["created_at"] = pd.to_datetime(df["created_at"], errors="coerce")

日期转完后也要检查:

python
bad_date = df[df["created_at"].isna()]
print("日期无法转换的行数:", len(bad_date))

ID 字段不要随便转数字

订单号、身份证号、手机号、资产编码这类字段即使看起来是数字,也经常应该保留字符串。

字段为什么不建议转数字
手机号可能有前导 0 或国际区号
身份证号可能包含 X,且不需要数学计算
资产编码编码不是数值,前导 0 有意义
订单号太长时可能被科学计数法影响

第四步:缺失值处理

缺失值处理不是“看到空就填 0”。要先判断业务含义。

mermaid
flowchart TD
    A["发现缺失值"] --> B{"字段是否必填"}
    B -- "必填" --> C["标记错误或剔除"]
    B -- "非必填" --> D{"缺失是否有业务默认值"}
    D -- "有" --> E["填默认值"]
    D -- "没有" --> F["保留为空或标记 unknown"]
    C --> G["输出错误报告"]
    E --> H["继续处理"]
    F --> H

常见策略:

字段处理建议原因
业务主键缺失剔除或进入错误文件无法追溯和去重
金额缺失通常不直接填 00 和未知不是一回事
备注缺失可以填空字符串不影响核心统计
年龄缺失可填中位数或 unknown看分析目的
日期缺失时间分析中通常剔除无法归属周期

示例:

python
required_columns = ["order_id", "amount", "created_at"]
invalid_required = df[df[required_columns].isna().any(axis=1)]
valid_df = df.dropna(subset=required_columns).copy()

第五步:重复值处理

重复不一定是完全重复。商业项目更常见的是“业务主键重复”。

python
duplicate_orders = df[df.duplicated(subset=["order_id"], keep=False)]
print(duplicate_orders.sort_values("order_id"))

去重前要明确规则:

规则适合场景
keep="first"第一条是原始记录,后续重复是误导入
keep="last"后一条代表更新后的状态
按更新时间排序后保留最新updated_at 字段
不自动去重,输出错误报告金额、状态冲突,需要人工核对

示例:按订单号保留最后一条。

python
df = df.sort_values("created_at")
df = df.drop_duplicates(subset=["order_id"], keep="last")

如果重复行金额或状态不一致,不要静默去重,应输出错误报告。

第六步:异常值处理

异常值要结合业务判断。

python
negative_paid = df[(df["status"] == "paid") & (df["amount"] < 0)]
too_large = df[df["amount"] > 100_000]
异常可能含义处理
已支付订单金额为负脏数据或状态错误输出错误报告
退款金额为负可能合理看退款口径
金额特别大大客户订单或录入错误抽样核对
日期在未来预约订单或错误日期看业务定义
状态不在枚举内脏数据标记错误

异常值不是一定删除。正确流程是:识别、标记、解释、按口径处理。

第七步:业务过滤和口径确认

比如统计“已支付销售额”,不能把所有订单都算进去。

python
paid_df = df[df["status"] == "paid"].copy()

如果业务要求排除测试城市、测试用户、内部订单,也要明确写入脚本:

python
paid_df = paid_df[~paid_df["city"].isin(["测试城市"])]

口径必须写清楚:

text
本报表销售额 = status 为 paid 的订单 amount 求和;
不包含 refund、cancelled、test 数据;
重复订单按 order_id 保留最后一条。

否则不同人写出来的报表会对不上。

第八步:分组聚合

按城市统计订单数和销售额:

python
summary = (
    paid_df
    .groupby("city", as_index=False)
    .agg(
        order_count=("order_id", "nunique"),
        total_amount=("amount", "sum"),
        avg_amount=("amount", "mean"),
    )
    .sort_values("total_amount", ascending=False)
)

as_index=False 的好处是导出后城市仍然是普通列。

常用聚合:

需求写法
计数count
去重计数nunique
求和sum
平均值mean
最大值max
中位数median

第九步:结果校验

导出之前必须校验。不要相信第一版结果。

python
raw_paid_total = paid_df["amount"].sum()
summary_total = summary["total_amount"].sum()

if abs(raw_paid_total - summary_total) > 0.0001:
    raise ValueError("聚合前后金额不一致")

还可以做:

校验目的
聚合前后总金额一致防止漏行或重复
分组数量和业务预期接近防止城市字段异常
抽样核对明细防止口径理解错
输出错误文件让脏数据可追溯
对比历史报表发现异常波动

第十步:导出和交付

python
summary.to_csv("city_amount_summary.csv", index=False, encoding="utf-8-sig")
invalid_required.to_csv("invalid_rows.csv", index=False, encoding="utf-8-sig")

为什么导出用 utf-8-sig

编码说明
utf-8通用编码,很多程序都支持
utf-8-sigExcel 打开中文 CSV 更不容易乱码
gbk一些中文 Windows 老系统常见

交付时建议同时给:

  1. 汇总结果文件。
  2. 错误数据文件。
  3. 处理脚本。
  4. 口径说明。
  5. 执行日志。

商业 Demo:订单数据清洗和城市销售汇总

下面 Demo 使用内存字符串模拟 CSV,可以直接运行。真实项目只需要把 StringIO 换成文件路径。

python
from io import StringIO
import pandas as pd


RAW_CSV = """order_id,city,amount,status,created_at
1001,北京,99.5,paid,2026-07-01
1002,上海,120元,paid,2026/07/01
1002,上海,120,paid,2026/07/01
1003,北京,,refund,unknown
1004,深圳,-80,paid,2026-07-02
1005,广州,300,paid,2026-07-02
"""


def load_orders() -> pd.DataFrame:
    return pd.read_csv(StringIO(RAW_CSV), dtype={"order_id": "string"})


def normalize_types(df: pd.DataFrame) -> pd.DataFrame:
    result = df.copy()
    result["amount_raw"] = result["amount"]
    result["amount"] = (
        result["amount"]
        .astype(str)
        .str.replace("元", "", regex=False)
        .str.strip()
    )
    result["amount"] = pd.to_numeric(result["amount"], errors="coerce")
    result["created_at"] = pd.to_datetime(result["created_at"], errors="coerce")
    return result


def split_invalid_rows(df: pd.DataFrame) -> tuple[pd.DataFrame, pd.DataFrame]:
    required = ["order_id", "city", "amount", "created_at", "status"]
    invalid_mask = (
        df[required].isna().any(axis=1)
        | ~df["status"].isin(["paid", "refund", "cancelled"])
        | ((df["status"] == "paid") & (df["amount"] < 0))
    )

    invalid = df[invalid_mask].copy()
    valid = df[~invalid_mask].copy()
    return valid, invalid


def deduplicate_orders(df: pd.DataFrame) -> pd.DataFrame:
    return (
        df.sort_values("created_at")
        .drop_duplicates(subset=["order_id"], keep="last")
        .copy()
    )


def summarize_city_sales(df: pd.DataFrame) -> pd.DataFrame:
    paid_df = df[df["status"] == "paid"].copy()
    return (
        paid_df
        .groupby("city", as_index=False)
        .agg(
            order_count=("order_id", "nunique"),
            total_amount=("amount", "sum"),
            avg_amount=("amount", "mean"),
        )
        .sort_values("total_amount", ascending=False)
    )


def main() -> None:
    raw = load_orders()
    typed = normalize_types(raw)
    deduped = deduplicate_orders(typed)
    valid, invalid = split_invalid_rows(deduped)
    summary = summarize_city_sales(valid)

    print("有效数据:")
    print(valid)
    print("\n异常数据:")
    print(invalid)
    print("\n城市汇总:")
    print(summary)


if __name__ == "__main__":
    main()

这个 Demo 要看懂几件事:

代码意义
dtype={"order_id": "string"}订单号按字符串处理,避免编码类字段被当数字
amount_raw保留原始金额,方便追溯
pd.to_numeric(..., errors="coerce")无法转换的金额变成缺失,再进入错误处理
pd.to_datetime(..., errors="coerce")无法转换的日期变成缺失
drop_duplicates按业务主键去重
split_invalid_rows不让脏数据直接进入汇总
groupby(...).agg(...)按城市统计多个指标

数据处理脚本怎么工程化

脚本不是写完一次就扔。商业项目建议至少有这些结构:

text
data_job/
  input/
    orders_raw.csv
  output/
    city_summary.csv
    invalid_rows.csv
  src/
    clean_orders.py
  tests/
    test_clean_orders.py
  README.md

工程化要求:

要求原因
参数化输入输出路径不要把文件名写死
输出错误数据脏数据要能反馈给业务
日志记录行数和耗时排查任务是否完整执行
核心清洗函数可测试口径变更时有回归保护
README 写口径别人知道你怎么算的
保留原始数据方便复核,不污染源文件

数据处理常见坑

后果正确做法
不看 info() 直接算字符串金额、日期错误都不知道先检查类型
空值全部填 0把未知伪装成 0按字段含义处理
完全重复去重业务主键重复仍存在按业务主键去重
静默删除异常行业务不知道数据有问题输出错误文件
只保存最终报表无法审计处理过程保存脚本、日志、口径
手工改 Excel不可复现、不可回滚用脚本处理
大文件一次读入内存爆掉分块读取或换数据库/分布式工具
金额用 float 做精确财务精度风险财务结算用 Decimal 或数据库 decimal

生产排查流程

数据报表对不上时,不要先改代码。按链路排查:

mermaid
flowchart TD
    A["报表结果不对"] --> B{"原始数据是否完整"}
    B -- "否" --> C["检查导出 SQL、时间范围、分页"]
    B -- "是" --> D{"读取行数是否正确"}
    D -- "否" --> E["检查编码、分隔符、表头"]
    D -- "是" --> F{"字段类型是否正确"}
    F -- "否" --> G["检查金额、日期、ID 转换"]
    F -- "是" --> H{"去重和过滤口径是否正确"}
    H -- "否" --> I["检查主键、状态、测试数据"]
    H -- "是" --> J{"聚合逻辑是否正确"}
    J -- "否" --> K["检查 groupby、口径、单位"]
    J -- "是" --> L["抽样对账和历史对比"]

排查证据:

证据用途
原始文件行数确认数据是否完整
读取后 df.shape确认读取是否正确
缺失值统计找到转换失败字段
重复主键数量判断是否重复计算
异常数据文件反馈数据质量问题
聚合前后总金额校验汇总是否漏数据
样例明细和业务人工核对

面试标准回答

Pandas 数据清洗的完整流程是什么?

可以这样答:

text
我会先确认字段含义和业务口径,再读取数据并检查 shape、head、info、缺失值和重复值。然后对金额、日期、状态等关键字段做类型转换和合法性校验,按业务主键去重,处理缺失值和异常值,再按明确口径过滤数据,最后 groupby 或 pivot 聚合并导出结果。导出前会做总数、总金额、样例抽查等校验,异常数据会单独输出,保证清洗过程可复现、可审计。

NumPy 和 Pandas 区别是什么?

NumPy 核心是 ndarray,适合高效数值计算、矩阵和向量化;Pandas 核心是 SeriesDataFrame,适合带行列标签的表格数据清洗、筛选、分组、聚合和导出。Pandas 很多底层计算依赖 NumPy,但它提供了更贴近业务表格的 API。

为什么数据处理不能只在 Excel 里手动改?

因为手工操作不可复现、不可审计、容易漏步骤,也无法自动处理新数据。Python 脚本可以把读取、清洗、校验、聚合、导出固化下来,后续数据来了可以重复执行,出错也能回看脚本和日志。

数据清洗中最容易出错的点是什么?

最常见的是字段类型错误、缺失值误填、重复数据没有按业务主键处理、异常值没有结合业务判断、过滤口径不清、聚合前后没有校验。生产数据处理的重点不是 API 背得多,而是能保证结果可信、过程可追溯。

关联知识点

知识点作用
NumPy 数组计算理解数组、向量化、广播、dtype
Pandas 清洗分析深入学习 DataFrame、缺失、重复、聚合
端到端实践从原始 CSV 到报表输出
Python 异常与文件理解文件读取、编码和异常处理
Python 日志与调试让数据处理脚本可排查
Python 测试给清洗函数写回归测试
Python 面试题查看标准回答和追问

本章小结

Python 数据处理的核心不是“会 Pandas”,而是建立一条可信数据流水线:理解口径、读取数据、检查质量、转换类型、处理缺失和重复、识别异常、按业务规则过滤、聚合统计、校验结果、导出并保留处理过程。只有这条链路说得清、跑得通、能复现,数据结果才值得被业务使用。