这个系列面向完全没有数据处理经验的同学。你不用会算法、不用懂机器学习,只要会一点点 Python 基础语法(变量、函数、for 循环),就能跟着把 12 篇走完。
系列更新节奏是每天一篇,顺序建议不要跳:
环境准备与第一个程序(本篇)
Series 与 DataFrame:把表格装进内存
读文件:CSV 与 Excel 的正确读法
数据探查:拿到一张表先看什么
筛选与排序
缺失值处理
数据清洗:类型、重复、异常值
分组聚合:Excel 透视表的代码版
表的合并与拼接
时间序列:按周按月做统计
数据分析常用可视化
综合实战:从脏数据到一份分析报告
一、为什么是 Pandas
先看一个很常见的场景:一张 3000 行的订单表,要按城市统计销售额,还要筛出金额异常的单子。用 Excel 也能做,但每次数据更新都要重新点一遍数据透视表,而且容易漏步骤。
用 Pandas 的做法是把这些操作写成代码:
import pandas as pd df = pd.read_csv("data/sales.csv") print(df.groupby("城市")["金额"].sum())跑一次是它,跑一百次还是它。数据换了,脚本不用改。
Pandas 是 Python 生态里最主流的数据处理库,优点很实在:
能装得下:几十万行的表格在内存里随便操作,不受 Excel 104 万行和手动操作的掣肘;
API 统一:读取、筛选、分组、合并、导出,都是同一套写法;
生态好:和 NumPy、Matplotlib、scikit-learn 无缝衔接,后面想画图、做分析、跑模型都不用换工具。
一句话:Pandas 是「用代码操作表格」的标准答案。
二、装 Python 和 Pandas
1. 选哪个 Python 发行版
两个选择,任选其一:
Anaconda:适合学生和新手,装完自带 Pandas、NumPy、Jupyter,省心,缺点是体积大(几个 GB);
官方 Python + pip 装包:体积小、干净,适合已经写过 Python 的同学。
本文按第二种来写,Anaconda 用户跳过安装步骤即可。
2. 装 Pandas
打开终端(Windows 是 CMD 或 PowerShell,macOS 是终端),执行:
pip install pandas openpyxl
pandas:数据处理主角;openpyxl:读写.xlsx文件要用它,很多人读 Excel 报错就是漏了这个包。
国内下载慢的话加个镜像:
pip install pandas openpyxl -i https://pypi.tuna.tsinghua.edu.cn/simple
3. 验证环境
python -c "import pandas as pd; print(pd.__version__)"
3.0.1
能打印出版本号(本文示例环境是3.0.1)就说明装好了。本系列代码在pandas 2.x / 3.x上都可以跑。
小提示:本系列的代码在 PyCharm、VS Code、Jupyter Notebook 里都能跑。新手建议先用 VS Code 或 PyCharm 建一个
.py文件,比 Notebook 更容易养成完整的脚本思维。
三、第一个程序:不读文件也能玩
不急着找数据,Pandas 可以直接用 Python 的字典造一张小表:
import pandas as pd df = pd.DataFrame( { "城市": ["杭州", "上海", "北京", "杭州"], "销量": [120, 95, 60, 88], } ) print(df) print("总销量:", df["销量"].sum())运行结果:
城市 销量 0 杭州 120 1 上海 95 2 北京 60 3 杭州 88 总销量: 363
四行代码就跑完了「造表 → 打印 → 求和」。df就是一张内存里的表格,df["销量"]取出其中一列。
四、Pandas 处理数据的固定套路
后面所有文章,其实都在重复这五步:
| 步骤 | 做什么 | 常用方法 |
|---|---|---|
| 1. 读取 | 把文件读成 DataFrame | read_csv/read_excel |
| 2. 查看 | 先看清数据长什么样 | head/info/describe |
| 3. 清洗 | 处理空值、重复、类型、异常 | dropna/fillna/astype |
| 4. 分析 | 筛选、排序、分组、合并 | 布尔索引 /groupby/merge |
| 5. 输出 | 导出结果或画图 | to_excel/plot |
记住这张表,你就有了一张「遇到问题该用哪个方法」的地图。
五、准备示例数据:一次生成,玩 11 篇
为了后面每篇都有真实数据可练,我们先写一个造数据的脚本mkdata.py。它会在当前目录生成data/文件夹,里面有销售、学生成绩、员工、订单、股票等几份 CSV,其中故意混入了空值、重复行和异常值,第 6、7 篇清洗时会用到。
新建mkdata.py,把下面代码完整粘进去:
# -*- coding: utf-8 -*- """生成系列文章里用的示例数据(练习数据 + 有瑕疵的脏数据)。""" import os import numpy as np import pandas as pd OUT = "data" def _ensure_out(): """确保输出目录存在,避免 to_csv 报“目录不存在”。""" os.makedirs(OUT, exist_ok=True) def make_sales(): """销售明细表:故意留了空值、重复行、异常值。""" _ensure_out() df = pd.DataFrame( { "订单日期": [ "2024-01-05", "2024-01-06", "2024-01-06", "2024-01-09", "2024-02-03", "2024-02-11", "2024-02-11", "2024-03-02", "2024-03-15", "2024-03-15", "2024-04-01", "2024-04-18", "2024-04-18", "2024-05-06", "2024-05-20", "2024-05-20", ], "城市": [ "杭州", "上海", "上海", "北京", "杭州", "广州", "广州", "北京", "上海", "上海", "杭州", None, "北京", "广州", "杭州", "杭州", ], "产品": [ "A-笔记本", "B-鼠标", "B-鼠标", "C-键盘", "A-笔记本", "B-鼠标", "B-鼠标", "A-笔记本", "C-键盘", "C-键盘", "B-鼠标", "A-笔记本", "D-显示器", "C-键盘", "D-显示器", "D-显示器", ], "数量": [3, 5, 5, 2, 4, 6, 6, 1, 7, 7, 2, 3, 2, 4, 5, 5], "单价": [ 49.9, 29.9, 29.9, 89.0, 49.9, 29.9, 29.9, 49.9, 89.0, 89.0, 29.9, 49.9, 599.0, 89.0, 599.0, 599.0, ], "销售员": [ "小张", "小李", "小李", "小王", "小张", "小赵", "小赵", "小王", "小李", "小李", "小张", "小赵", "小王", "小李", "小张", None, ], } ) df["金额"] = (df["数量"] * df["单价"]).round(2) df.loc[[5, 12], "金额"] = np.nan # 模拟系统没算出金额 df.loc[14, "金额"] = -599.0 # 模拟录入错误 df.to_csv(f"{OUT}/sales.csv", index=False, encoding="utf-8-sig") return df def make_students(): """学生成绩表:有缺失成绩、重复学号、文本型数字。""" _ensure_out() df = pd.DataFrame( { "学号": [ "2024001", "2024002", "2024003", "2024004", "2024005", "2024005", "2024006", "2024007", "2024008", "2024009", "2024010", "2024011", ], "姓名": [ "张小明", "李小红", "王大力", "赵小美", "陈小强", "陈小强", "刘小燕", "杨小帆", "周小雨", "吴小磊", "郑小云", "孙小虎", ], "班级": [ "一班", "一班", "二班", "二班", "一班", "一班", "三班", "三班", "二班", "三班", "一班", "三班", ], "性别": [ "男", "女", "男", "女", "男", "男", "女", "男", "女", "男", "女", "男", ], "语文": [88, 92, None, 79, 95, 95, 68, "73", 84, 90, 76, 61], "数学": [92, 85, 78, 88, None, None, 71, 65, 90, 94, 69, 58], "英语": [85, 90, 82, None, 88, 88, 74, 80, 86, 91, 72, 66], } ) df.to_csv(f"{OUT}/students.csv", index=False, encoding="utf-8-sig") return df def make_employees(): """员工表:数字列里混进了文本和空值。""" _ensure_out() df = pd.DataFrame( { "工号": ["E01", "E02", "E03", "E04", "E05", "E06", "E07", "E08"], "姓名": ["张伟", "王芳", "李娜", "刘洋", "陈静", "杨波", "赵敏", "周涛"], "部门": ["技术部", "市场部", "技术部", "人事部", "市场部", "技术部", "人事部", "市场部"], "入职日期": [ "2021-03-01", "2020-07-15", "2022-09-01", "2019-05-20", "2023-01-09", "2021-11-03", "2022-04-18", "2023-06-26", ], "月薪": ["12000", "9500", "15000", "8000", None, "18000", "8500", "10500"], "绩效分": [4.5, 3.8, 4.8, 3.5, 4.0, 4.9, 3.9, 4.2], } ) df.to_csv(f"{OUT}/employees.csv", index=False, encoding="utf-8-sig") return df def make_orders_messy(): """订单表 + 客户表,用来练 merge 和 concat。""" _ensure_out() orders = pd.DataFrame( { "订单号": ["A001", "A002", "A003", "A004", "A005", "A006"], "客户ID": ["C01", "C02", "C03", "C02", "C05", "C01"], "下单日期": [ "2024-01-08", "2024-01-19", "2024-02-02", "2024-02-23", "2024-03-05", "2024-03-21", ], "订单金额": [320.0, 158.5, 990.0, 420.0, 88.0, 265.5], } ) customers = pd.DataFrame( { "客户ID": ["C01", "C02", "C03", "C04"], "客户名称": ["恒信科技", "云图文化", "海通物流", "光年传媒"], "所在城市": ["杭州", "上海", "北京", "广州"], "客户等级": ["A", "B", "A", "C"], } ) orders.to_csv(f"{OUT}/orders.csv", index=False, encoding="utf-8-sig") customers.to_csv(f"{OUT}/customers.csv", index=False, encoding="utf-8-sig") return orders, customers def make_orders_xlsx(): """Excel 双 sheet:明细 + 按城市汇总。""" _ensure_out() df = make_sales() with pd.ExcelWriter(f"{OUT}/sales.xlsx", engine="openpyxl") as writer: df.to_excel(writer, sheet_name="明细", index=False) ( df.groupby("城市", dropna=False) .agg(订单数=("数量", "size"), 销量=("数量", "sum"), 销售额=("金额", "sum")) .reset_index() .to_excel(writer, sheet_name="按城市汇总", index=False) ) def make_stock(): """时间序列:某只股票 30 个交易日的收盘价。""" _ensure_out() rng = np.random.default_rng(7) days = pd.bdate_range("2024-01-02", periods=30) close = 100 + np.cumsum(rng.normal(0, 1.6, len(days))).round(2) df = pd.DataFrame( {"日期": days, "收盘价": close, "成交量": rng.integers(8000, 26000, len(days))} ) df.to_csv(f"{OUT}/stock.csv", index=False, encoding="utf-8-sig") return df def main(): _ensure_out() make_sales() make_students() make_employees() make_orders_messy() make_orders_xlsx() make_stock() print("示例数据已生成到 ./data 目录:") for name in sorted(os.listdir(OUT)): print(" -", name) if __name__ == "__main__": main()然后运行它:
import runpy # 以 __main__ 身份执行脚本,等价于在命令行敲 python mkdata.py runpy.run_path("mkdata.py", run_name="__main__")看到下面这样的输出就成功了:
示例数据已生成到 ./data 目录: - customers.csv - employees.csv - orders.csv - sales.csv - sales.xlsx - stock.csv - students.csv
顺手验证一下数据能读进来:
import pandas as pd df = pd.read_csv("data/sales.csv", encoding="utf-8-sig") print(df.shape) print(df.head(3))(16, 7) 订单日期 城市 产品 数量 单价 销售员 金额 0 2024-01-05 杭州 A-笔记本 3 49.9 小张 149.7 1 2024-01-06 上海 B-鼠标 5 29.9 小李 149.5 2 2024-01-06 上海 B-鼠标 5 29.9 小李 149.5
(16, 7)表示 16 行 7 列,说明数据已经躺在 Python 里了。
六、新手最容易踩的三个坑
1. 报错ModuleNotFoundError: No module named 'pandas'
库没装上,或者装到了另一个 Python 环境里。先执行python -m pip install pandas,确认python -c "import pandas"能过。
2. 读取 Excel 报错Missing optional dependency 'openpyxl'
按提示补装即可:pip install openpyxl。
3. 打印中文乱码 / 读 CSV 报 UnicodeDecodeError
Windows 终端默认是 GBK 编码,而数据文件通常是 UTF-8。两个处理办法:
读取时显式指定编码:
pd.read_csv("data/sales.csv", encoding="utf-8-sig")(本系列统一这么写);或者在脚本开头加
import sys; sys.stdout.reconfigure(encoding="utf-8")。
小结
这一篇做了三件事:装好 Pandas、跑通第一个程序、生成一份贯穿全系列的示例数据。
下一篇开始正式进入正题:Series 和 DataFrame 到底是什么,怎么取一列、取一行、按条件取一片。把这两个对象的脾气摸清了,后面所有操作都会顺很多。