Week 03:NumPy / Pandas 表格数据处理
返回主线计划:USTC 统计学 AI / 量化 20 周成长计划
本文命令默认使用 fish shell。本周从“会写 Python 脚本”过渡到“能处理真实表格数据”。对统计学同学来说,NumPy / Pandas 是以后做统计建模、机器学习、时间序列、量化金融的共同地基。
0. 本周详细教学:语法、规范、验收
本节不是追加在尾部的复习,而是本周正文的入口。先读这里,再做后面的命令和项目。
0.1 本周真正要学会什么
| 维度 | 要求 |
|---|---|
| 知识点 | ndarray、DataFrame、缺失值、groupby、merge |
| 代码语法 | 能从空文件写出本周核心脚本,而不是只复制运行 |
| 程序规范 | 函数拆分、路径清楚、输入输出明确、错误能解释 |
| 交付物 | notebooks/week03_pandas_eda.ipynb |
| 验收方式 | 从 fish 终端运行命令,得到可复查的文件或指标 |
0.2 代码语法精讲
下面的代码不是最终答案,而是本周必须理解的最小骨架:
import pandas as pd
df = pd.read_csv(“data/raw.csv”) df.columns = [c.strip().lower().replace(” ”, ”_”) for c in df.columns] missing = df.isna().sum().sort_values(ascending=False) summary = df.groupby(“category”).agg(n=(“value”, “size”), mean_value=(“value”, “mean”)) print(missing) print(summary)
读代码时按四步检查:输入从哪里来;中间变量的类型和 shape 是什么;函数或脚本输出什么;哪些错误应该显式报出来。
0.3 本周程序规范
- 所有路径用相对路径或 `pathlib.Path`,不要写死 `/home/miku/...`。
- 核心逻辑进 `src/`,notebook 只做探索和解释。
- 每个脚本能从 fish 终端运行,并在 README 写出命令。
- 输出必须落盘到 `reports/`、`figures/` 或 `outputs/`,不能只在屏幕上看。
0.4 本周练习分层
| 层级 | 任务 | 不合格表现 | 合格验收 |
|---|---|---|---|
| 最小练习 | 手写上面的最小骨架 | 只在 notebook 里运行 | 终端运行成功 |
| 标准练习 | 把逻辑拆成函数/模块 | 一个大脚本从头写到尾 | 至少 2 个函数,职责清楚 |
| 项目练习 | 生成本周交付物 notebooks/week03_pandas_eda.ipynb | 只有屏幕输出 | 文件落盘,可复查 |
| 复盘练习 | 写 3 个错误和修复 | 只写“已解决” | 写清报错、原因、修复、预防 |
0.5 本周和主线的连接
- 回到总计划:USTC AI / Quant 练习手册
- 查详细练习索引:技术练习详解
- 查质量评分:最终质量门槛
1. 本周目标
完成 Week 03 后,你应该能:
- 理解 NumPy array 和 Python list 的区别。
- 用 Pandas
DataFrame表示表格数据。 - 读取 CSV、查看数据结构、保存清洗后的 CSV。
- 识别和处理缺失值。
- 使用
groupby做分组统计。 - 做一轮简单 EDA:行列数、字段类型、描述统计、缺失值、分组均值。
- 输出一个可复现的数据处理脚本和一份简短数据报告。
文件布局
建议本周交付物:
week03-numpy-pandas/
├── data/
│ ├── raw/
│ │ └── student_scores.csv
│ └── processed/
│ └── student_scores_clean.csv
├── notebooks/
│ └── week03_pandas_eda.ipynb
├── reports/
│ └── week03_data_report.md
├── src/
│ └── clean_scores.py
├── .venv/
├── pyproject.toml
└── README.md
Notebook 可以用来探索;脚本用来固定最终流程。以后项目展示时,脚本比“只保存在 notebook 里的零散代码”更容易复现。
2. 前置要求
你需要已经具备:
- 会在 fish 终端里
cd到项目目录。 - 会使用
uv init、uv venv、uv add。 - 知道 list / dict / function 的基本用法。
- 能运行
uv run python script.py。 - VS Code 可以打开项目目录。
检查基础命令:
python --version
uv --version
pwd
如果 Python 或 uv 找不到,先回到 Week 01 环境搭建教程检查。
3. 创建 Week 03 项目
mkdir -p ~/Code/python-learning/week03-numpy-pandas
cd ~/Code/python-learning/week03-numpy-pandas
mkdir -p src data/raw data/processed notebooks reports
uv init
uv venv
source .venv/bin/activate.fish
uv add numpy pandas
code .
解释:
| 命令 | 作用 |
|---|---|
mkdir -p ~/Code/python-learning/week03-numpy-pandas |
创建本周项目目录 |
mkdir -p src data/raw data/processed notebooks reports |
区分原始数据、清洗后数据、代码和报告 |
uv init |
初始化 Python 项目配置 |
uv venv |
创建 .venv 虚拟环境 |
source .venv/bin/activate.fish |
fish 中激活虚拟环境 |
uv add numpy pandas |
安装 NumPy 和 Pandas |
code . |
用 VS Code 打开当前项目 |
检查:
which python
uv run python -c "import numpy, pandas; print(numpy.__version__); print(pandas.__version__)"
如果能打印版本号,环境可用。
4. 准备一个练习 CSV
先用小数据理解流程。创建 data/raw/student_scores.csv。
你可以在 VS Code 手动创建,也可以用 fish:
printf 'student,major,gender,math,english,python,study_hours\nA,statistics,F,88,79,85,12\nB,statistics,M,92,81,90,14\nC,finance,F,75,86,78,9\nD,finance,M,80,,82,8\nE,statistics,F,95,88,,15\nF,computer,M,70,76,88,10\nG,computer,F,,90,91,11\nH,statistics,M,84,82,86,13\n' > data/raw/student_scores.csv
检查文件:
head data/raw/student_scores.csv
wc -l data/raw/student_scores.csv
说明:
- 第一行是列名。
- 有些成绩故意留空,用来练习缺失值处理。
major和gender是分类变量。math、english、python、study_hours是数值变量。
5. NumPy array 入门
NumPy 的核心对象是 array。它适合做数值计算,尤其是向量、矩阵、批量运算。
创建 src/numpy_intro.py:
import numpy as npscores = np.array([88, 92, 75, 80, 95])
print(scores) print(scores.mean()) print(scores.std()) print(scores >= 80) print(scores[scores >= 80])
运行:
uv run python src/numpy_intro.py
你会看到:
scores.mean()计算均值。scores.std()计算标准差。scores >= 80返回布尔数组。scores[scores >= 80]用布尔条件筛选数组。
Python list 和 NumPy array 的一个重要区别:
python_list = [1, 2, 3] numpy_array = np.array([1, 2, 3])
print(python_list * 2) print(numpy_array * 2)
输出含义:
[1, 2, 3, 1, 2, 3] # list 的 * 2 是重复
[2 4 6] # array 的 * 2 是逐元素乘法
这就是为什么数据分析和机器学习大量使用 NumPy。
6. Pandas DataFrame 入门
Pandas 的核心对象是 DataFrame,可以理解为“带列名和索引的表格”。
创建 src/pandas_intro.py:
import pandas as pdstudents = pd.DataFrame( { “student”: [“A”, “B”, “C”], “major”: [“statistics”, “statistics”, “finance”], “math”: [88, 92, 75], “python”: [85, 90, 78], } )
print(students) print(students[“math”]) print(students[[“student”, “python”]]) print(students[students[“math”] >= 80])
运行:
uv run python src/pandas_intro.py
常见操作:
| 写法 | 作用 |
|---|---|
df.head() |
查看前 5 行 |
df.tail() |
查看后 5 行 |
df.shape |
查看行数和列数 |
df.columns |
查看列名 |
df.dtypes |
查看每列数据类型 |
df["math"] |
取单列,结果是 Series |
df[["math", "python"]] |
取多列,结果是 DataFrame |
df[df["math"] >= 80] |
按条件筛选行 |
7. 读取 CSV 并快速查看
创建 src/read_scores.py:
from pathlib import Pathimport pandas as pd
path = Path(“data/raw/student_scores.csv”) df = pd.read_csv(path)
print(“Shape:”, df.shape) print(“Columns:”, list(df.columns)) print(“Head:”) print(df.head()) print(“Data types:”) print(df.dtypes)
运行:
uv run python src/read_scores.py
解释:
| 代码 | 作用 |
|---|---|
Path("data/raw/student_scores.csv") |
用 pathlib 表示路径 |
pd.read_csv(path) |
读取 CSV 为 DataFrame |
df.shape |
输出 (行数, 列数) |
df.head() |
快速看前几行,确认读入正确 |
df.dtypes |
判断数值列是否真的被识别为数值 |
读取真实数据后,第一步永远不是建模,而是看:
print(df.head())
print(df.info())
print(df.describe())
8. 缺失值检查
在数据分析里,缺失值不是小事。它会影响均值、相关性、模型训练和图表解释。
创建 src/missing_values.py:
from pathlib import Pathimport pandas as pd
path = Path(“data/raw/student_scores.csv”) df = pd.read_csv(path)
missing_by_column = df.isna().sum() missing_total = int(df.isna().sum().sum()) missing_rate = df.isna().mean().sort_values(ascending=False)
print(“Missing values by column:”) print(missing_by_column) print(“Total missing values:”, missing_total) print(“Missing rate:”) print(missing_rate)
运行:
uv run python src/missing_values.py
常用方法:
| 写法 | 作用 |
|---|---|
df.isna() |
判断每个单元格是否缺失 |
df.isna().sum() |
每列缺失数量 |
df.isna().mean() |
每列缺失比例 |
df.dropna() |
删除包含缺失值的行 |
df.fillna(value) |
用指定值填补缺失 |
初学阶段可以使用简单策略:
- 数值成绩列:用该列均值或中位数填补。
- 分类列:用众数或
"unknown"填补。 - 缺失太多的字段:在报告中说明,暂时不用于分析。
9. 清洗数据并保存 CSV
创建 src/clean_scores.py:
from pathlib import Pathimport pandas as pd
RAW_PATH = Path(“data/raw/student_scores.csv”) OUTPUT_PATH = Path(“data/processed/student_scores_clean.csv”) NUMERIC_COLUMNS = [“math”, “english”, “python”, “study_hours”]
def load_data(path: Path) -> pd.DataFrame: return pd.read_csv(path)
def clean_data(df: pd.DataFrame) -> pd.DataFrame: cleaned = df.copy()
plain for column in NUMERIC_COLUMNS: median_value = cleaned[column].median() cleaned[column] = cleaned[column].fillna(median_value)
plain cleaned[“average_score”] = cleaned[[“math”, “english”, “python”]].mean(axis=1) cleaned[“passed_python”] = cleaned[“python”] >= 80
plain return cleaned
def save_data(df: pd.DataFrame, path: Path) -> None: path.parent.mkdir(parents=True, exist_ok=True) df.to_csv(path, index=False)
def main() -> None: df = load_data(RAW_PATH) cleaned = clean_data(df) save_data(cleaned, OUTPUT_PATH) print(f”Saved cleaned data to {OUTPUT_PATH}”) print(cleaned.head())
if name == “main”: main()
运行:
uv run python src/clean_scores.py
ls data/processed
head data/processed/student_scores_clean.csv
重点解释:
| 代码 | 作用 |
|---|---|
cleaned = df.copy() |
不直接修改原始 DataFrame,保留原数据 |
median() |
用中位数作为简单填补值,抗极端值能力比均值稍好 |
fillna(median_value) |
填补缺失成绩 |
mean(axis=1) |
对每一行计算平均分 |
to_csv(..., index=False) |
保存 CSV 时不额外写入索引列 |
10. groupby 分组统计
groupby 是 Pandas 最重要的操作之一。统计学同学可以把它理解为:按某个分类变量分组,然后对每组计算统计量。
创建 src/groupby_scores.py:
from pathlib import Pathimport pandas as pd
path = Path(“data/processed/student_scores_clean.csv”) df = pd.read_csv(path)
major_summary = df.groupby(“major”)[[“math”, “english”, “python”, “average_score”]].mean() gender_summary = df.groupby(“gender”)[“average_score”].agg([“count”, “mean”, “std”, “min”, “max”])
print(“Average scores by major:”) print(major_summary) print() print(“Average score distribution by gender:”) print(gender_summary)
运行:
uv run python src/groupby_scores.py
解释:
| 写法 | 含义 |
|---|---|
df.groupby("major") |
按专业分组 |
[["math", "english"]] |
选择要统计的列 |
.mean() |
对每组求均值 |
.agg(["count", "mean", "std"]) |
一次计算多个统计量 |
注意:分组统计是观察,不是因果结论。比如某专业平均分更高,不能直接说“专业导致成绩更高”,还要考虑样本量、选课背景、数据来源等。
11. merge 简介
计划里提到 merge。本周先理解它的用途:把两张表按共同键合并。
创建 data/raw/major_info.csv:
printf 'major,school\nstatistics,School of Management\nfinance,School of Management\ncomputer,School of Computer Science\n' > data/raw/major_info.csv
创建 src/merge_intro.py:
from pathlib import Pathimport pandas as pd
scores = pd.read_csv(Path(“data/processed/student_scores_clean.csv”)) major_info = pd.read_csv(Path(“data/raw/major_info.csv”))
merged = scores.merge(major_info, on=“major”, how=“left”) print(merged[[“student”, “major”, “school”, “average_score”]])
运行:
uv run python src/merge_intro.py
常见合并方式:
| 参数 | 含义 |
|---|---|
on="major" |
两张表都用 major 列作为键 |
how="left" |
保留左表所有行,右表匹配不到则为缺失 |
how="inner" |
只保留两边都匹配到的行 |
12. 时间索引简介
时间序列和量化金融会大量使用时间索引。本周先学会把日期列转成时间类型并设为索引。
创建 data/raw/daily_price.csv:
printf 'date,close,volume\n2026-07-20,10.2,1200\n2026-07-21,10.5,1500\n2026-07-22,10.1,1100\n2026-07-23,10.8,1800\n' > data/raw/daily_price.csv
创建 src/time_index_intro.py:
from pathlib import Pathimport pandas as pd
price = pd.read_csv(Path(“data/raw/daily_price.csv”)) price[“date”] = pd.to_datetime(price[“date”]) price = price.set_index(“date”) price[“daily_return”] = price[“close”].pct_change()
print(price) print(price.loc[“2026-07-21”:“2026-07-23”])
运行:
uv run python src/time_index_intro.py
重点:
pd.to_datetime把字符串日期转成时间类型。set_index("date")把日期作为索引。pct_change()计算相邻时期的变化率,是量化和时间序列的常见起点。
13. 简单 EDA 脚本
创建 src/simple_eda.py,把常用检查固定下来:
from pathlib import Pathimport pandas as pd
path = Path(“data/processed/student_scores_clean.csv”) df = pd.read_csv(path)
print(“1. Shape”) print(df.shape) print()
print(“2. Columns”) print(list(df.columns)) print()
print(“3. Data types”) print(df.dtypes) print()
print(“4. Missing values”) print(df.isna().sum()) print()
print(“5. Descriptive statistics”) print(df.describe()) print()
print(“6. Group summary by major”) print(df.groupby(“major”)[“average_score”].agg([“count”, “mean”, “std”, “min”, “max”]))
运行:
uv run python src/simple_eda.py
这份输出可以直接作为 reports/week03_data_report.md 的素材。
14. Notebook vs script:怎么选
本周计划的交付物包含 notebook,但你也应该保留脚本。
| 工具 | 适合做什么 | 不适合做什么 |
|---|---|---|
| Notebook | 逐步探索、看表格、尝试图表、写观察 | 长期维护的完整清洗流程 |
| Script | 固化流程、复现结果、命令行运行、Git diff 清楚 | 交互式尝试和即时展示 |
推荐工作方式:
1. 在 notebook 中探索:看字段、缺失值、分布、分组统计。
2. 把确认有效的步骤整理到 src/clean_scores.py。
3. 用 src/simple_eda.py 输出稳定统计结果。
4. 在 reports/week03_data_report.md 写结论。
如果使用 VS Code Notebook:
uv add ipykernel
然后在 VS Code 中新建 notebooks/week03_pandas_eda.ipynb,选择当前 .venv 作为 kernel。
15. 报告结构 reports/week03_data_report.md
在 VS Code 创建报告文件,建议结构:
# Week 03 数据处理报告1. 数据来源
说明数据来自练习 CSV,字段包含学生、专业、性别、数学、英语、Python、学习时长。
2. 数据规模
- 原始数据行数:8
- 原始数据列数:7
- 清洗后数据行数:8
- 清洗后数据列数:9
3. 缺失值处理
说明哪些列有缺失值,以及使用中位数填补数值列。
4. 描述统计
摘录
df.describe()中重要结果,例如各科均值、标准差、最小值、最大值。5. 分组统计
按 major 比较 average_score,注意样本很小,只能作为练习观察。
6. 下一步
Week 04 将对清洗后的数据做可视化,生成直方图、散点图、箱线图和相关性热力图。
报告应该写“观察到了什么”和“不能过度解释什么”。这是统计学训练的一部分。
16. 本周练习
练习 A:NumPy 基础
创建 src/numpy_exercises.py:
- 创建数组
[1, 2, 3, 4, 5]。 - 计算均值、标准差、最大值、最小值。
- 筛选出大于 3 的元素。
- 创建一个 2 行 3 列矩阵,并计算每列均值。
运行:
uv run python src/numpy_exercises.py
练习 B:Pandas 筛选
在 student_scores_clean.csv 中筛选:
major == "statistics"的学生。python >= 85的学生。study_hours >= 12且average_score >= 85的学生。
练习 C:缺失值策略比较
分别尝试:
- 删除缺失值行。
- 用均值填补。
- 用中位数填补。
比较每种策略下 average_score 的均值是否变化。
练习 D:分组统计
按 major 和 gender 分组,计算:
- 样本数。
average_score均值。python均值。
提示:可以使用:
df.groupby(["major", "gender"])[["average_score", "python"]].mean()
17. 验收检查
在项目根目录执行:
pwd
ls
source .venv/bin/activate.fish
uv run python -c "import numpy, pandas; print('ok')"
uv run python src/clean_scores.py
uv run python src/simple_eda.py
ls data/processed
head data/processed/student_scores_clean.csv
验收标准:
- 当前目录是
week03-numpy-pandas。 source .venv/bin/activate.fish不报错。- NumPy / Pandas 能成功 import。
src/clean_scores.py能生成data/processed/student_scores_clean.csv。src/simple_eda.py能输出 shape、columns、dtypes、missing values、describe、groupby。- 报告
reports/week03_data_report.md能解释数据来源、缺失值处理、描述统计和分组统计。
18. 常见错误
错误 1:ModuleNotFoundError: No module named 'pandas'
说明当前环境没有 pandas,或运行时没有使用 uv 项目环境。
修复:
cd ~/Code/python-learning/week03-numpy-pandas
uv add pandas numpy
uv run python src/read_scores.py
错误 2:FileNotFoundError: data/raw/student_scores.csv
多数情况是当前目录错了。
检查:
pwd
ls data/raw
如果不在项目根目录,执行:
cd ~/Code/python-learning/week03-numpy-pandas
错误 3:保存 CSV 后多了一列 Unnamed: 0
原因:保存时把 DataFrame 索引也写进 CSV。
修复:
df.to_csv(path, index=False)
错误 4:均值计算报错或结果奇怪
原因可能是数字列被读成字符串,或者包含非数字符号。
检查:
print(df.dtypes)
必要时转换:
df["math"] = pd.to_numeric(df["math"], errors="coerce")
errors="coerce" 会把无法转换的值变成缺失值,然后再统一处理。
错误 5:Notebook 找不到当前虚拟环境
先安装 kernel 支持:
uv add ipykernel
然后在 VS Code Notebook 右上角选择当前项目 .venv 对应的 Python kernel。
19. 下一步
Week 04 会把本周清洗后的表格数据转成可读图表:直方图看分布,散点图看关系,箱线图看组间差异,相关性热力图看变量关联。最终目标是写出一份结构清楚的 EDA 报告,而不是只生成几张图片。
plain
If you enjoyed this, leave a comment~