Week 03:NumPy / Pandas 表格数据处理

Published 2026-07-26 03:00 5102 words 26 min read

This post is not yet available in English. Showing the original.
Week 03:NumPy / Pandas 表格数据处理。fish-first 终端教学,面向 CachyOS、VS Code、uv 和 Python 学习路线。
Oh My Pi / weekly tutorial / week03-numpy-pandas
miku@cachyos:~/Code/python-learning$ omp teach week03-numpy-pandas --fish-first --step-by-step
source739 行教学文档
weekWeek 03
shellfish-first 命令版
backlink20 周计划

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 代码语法精讲

下面的代码不是最终答案,而是本周必须理解的最小骨架:

omppython
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 本周和主线的连接

1. 本周目标

完成 Week 03 后,你应该能:

  1. 理解 NumPy array 和 Python list 的区别。
  2. 用 Pandas DataFrame 表示表格数据。
  3. 读取 CSV、查看数据结构、保存清洗后的 CSV。
  4. 识别和处理缺失值。
  5. 使用 groupby 做分组统计。
  6. 做一轮简单 EDA:行列数、字段类型、描述统计、缺失值、分组均值。
  7. 输出一个可复现的数据处理脚本和一份简短数据报告。

文件布局

建议本周交付物:

ompprompt
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 inituv venvuv add
  • 知道 list / dict / function 的基本用法。
  • 能运行 uv run python script.py
  • VS Code 可以打开项目目录。

检查基础命令:

ompfish
python --version
uv --version
pwd

如果 Python 或 uv 找不到,先回到 Week 01 环境搭建教程检查。

3. 创建 Week 03 项目

ompfish
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 打开当前项目

检查:

ompfish
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:

ompfish
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

检查文件:

ompfish
head data/raw/student_scores.csv
wc -l data/raw/student_scores.csv

说明:

  • 第一行是列名。
  • 有些成绩故意留空,用来练习缺失值处理。
  • majorgender 是分类变量。
  • mathenglishpythonstudy_hours 是数值变量。

5. NumPy array 入门

NumPy 的核心对象是 array。它适合做数值计算,尤其是向量、矩阵、批量运算。

创建 src/numpy_intro.py

omppython
import numpy as np

scores = np.array([88, 92, 75, 80, 95])

print(scores) print(scores.mean()) print(scores.std()) print(scores >= 80) print(scores[scores >= 80])

运行:

ompfish
uv run python src/numpy_intro.py

你会看到:

  • scores.mean() 计算均值。
  • scores.std() 计算标准差。
  • scores >= 80 返回布尔数组。
  • scores[scores >= 80] 用布尔条件筛选数组。

Python list 和 NumPy array 的一个重要区别:

omppython
python_list = [1, 2, 3]
numpy_array = np.array([1, 2, 3])

print(python_list * 2) print(numpy_array * 2)

输出含义:

ompprompt
[1, 2, 3, 1, 2, 3]  # list 的 * 2 是重复
[2 4 6]             # array 的 * 2 是逐元素乘法

这就是为什么数据分析和机器学习大量使用 NumPy。

6. Pandas DataFrame 入门

Pandas 的核心对象是 DataFrame,可以理解为“带列名和索引的表格”。

创建 src/pandas_intro.py

omppython
import pandas as pd

students = 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])

运行:

ompfish
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

omppython
from pathlib import Path

import 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)

运行:

ompfish
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 判断数值列是否真的被识别为数值

读取真实数据后,第一步永远不是建模,而是看:

omppython
print(df.head())
print(df.info())
print(df.describe())

8. 缺失值检查

在数据分析里,缺失值不是小事。它会影响均值、相关性、模型训练和图表解释。

创建 src/missing_values.py

omppython
from pathlib import Path

import 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)

运行:

ompfish
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

omppython
from pathlib import Path

import 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()

运行:

ompfish
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

omppython
from pathlib import Path

import 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)

运行:

ompfish
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

ompfish
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

omppython
from pathlib import Path

import 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”]])

运行:

ompfish
uv run python src/merge_intro.py

常见合并方式:

参数 含义
on="major" 两张表都用 major 列作为键
how="left" 保留左表所有行,右表匹配不到则为缺失
how="inner" 只保留两边都匹配到的行

12. 时间索引简介

时间序列和量化金融会大量使用时间索引。本周先学会把日期列转成时间类型并设为索引。

创建 data/raw/daily_price.csv

ompfish
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

omppython
from pathlib import Path

import 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”])

运行:

ompfish
uv run python src/time_index_intro.py

重点:

  • pd.to_datetime 把字符串日期转成时间类型。
  • set_index("date") 把日期作为索引。
  • pct_change() 计算相邻时期的变化率,是量化和时间序列的常见起点。

13. 简单 EDA 脚本

创建 src/simple_eda.py,把常用检查固定下来:

omppython
from pathlib import Path

import 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”]))

运行:

ompfish
uv run python src/simple_eda.py

这份输出可以直接作为 reports/week03_data_report.md 的素材。

14. Notebook vs script:怎么选

本周计划的交付物包含 notebook,但你也应该保留脚本。

工具 适合做什么 不适合做什么
Notebook 逐步探索、看表格、尝试图表、写观察 长期维护的完整清洗流程
Script 固化流程、复现结果、命令行运行、Git diff 清楚 交互式尝试和即时展示

推荐工作方式:

ompprompt
1. 在 notebook 中探索:看字段、缺失值、分布、分组统计。
2. 把确认有效的步骤整理到 src/clean_scores.py。
3. 用 src/simple_eda.py 输出稳定统计结果。
4. 在 reports/week03_data_report.md 写结论。

如果使用 VS Code Notebook:

ompfish
uv add ipykernel

然后在 VS Code 中新建 notebooks/week03_pandas_eda.ipynb,选择当前 .venv 作为 kernel。

15. 报告结构 reports/week03_data_report.md

在 VS Code 创建报告文件,建议结构:

ompmarkdown
# 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. 创建数组 [1, 2, 3, 4, 5]
  2. 计算均值、标准差、最大值、最小值。
  3. 筛选出大于 3 的元素。
  4. 创建一个 2 行 3 列矩阵,并计算每列均值。

运行:

ompfish
uv run python src/numpy_exercises.py

练习 B:Pandas 筛选

student_scores_clean.csv 中筛选:

  1. major == "statistics" 的学生。
  2. python >= 85 的学生。
  3. study_hours >= 12average_score >= 85 的学生。

练习 C:缺失值策略比较

分别尝试:

  • 删除缺失值行。
  • 用均值填补。
  • 用中位数填补。

比较每种策略下 average_score 的均值是否变化。

练习 D:分组统计

majorgender 分组,计算:

  • 样本数。
  • average_score 均值。
  • python 均值。

提示:可以使用:

omppython
df.groupby(["major", "gender"])[["average_score", "python"]].mean()

17. 验收检查

在项目根目录执行:

ompfish
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 项目环境。

修复:

ompfish
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

多数情况是当前目录错了。

检查:

ompfish
pwd
ls data/raw

如果不在项目根目录,执行:

ompfish
cd ~/Code/python-learning/week03-numpy-pandas

错误 3:保存 CSV 后多了一列 Unnamed: 0

原因:保存时把 DataFrame 索引也写进 CSV。

修复:

omppython
df.to_csv(path, index=False)

错误 4:均值计算报错或结果奇怪

原因可能是数字列被读成字符串,或者包含非数字符号。

检查:

omppython
print(df.dtypes)

必要时转换:

omppython
df["math"] = pd.to_numeric(df["math"], errors="coerce")

errors="coerce" 会把无法转换的值变成缺失值,然后再统一处理。

错误 5:Notebook 找不到当前虚拟环境

先安装 kernel 支持:

ompfish
uv add ipykernel

然后在 VS Code Notebook 右上角选择当前项目 .venv 对应的 Python kernel。

19. 下一步

Week 04 会把本周清洗后的表格数据转成可读图表:直方图看分布,散点图看关系,箱线图看组间差异,相关性热力图看变量关联。最终目标是写出一份结构清楚的 EDA 报告,而不是只生成几张图片。

plain

If you enjoyed this, leave a comment~

© 2026 江无没有月 @miku
Powered by theme astro-koharu · Inspired by Shoka