03 · Pandas 数据处理与清洗

数据科学⭐⭐⭐⭐⭐核心必修|约 9 分钟16 节1 图
本章目标

熟练使用 Pandas 完成数据读取、清洗、筛选、分组聚合、合并连接与透视表。GEO 表达矩阵、中药成分-靶点表、临床数据表,都靠 Pandas 处理。

本章路线
Pandas 数据处理流水线
图 03-1Pandas 数据处理流水线
03 · Pandas 数据处理与清洗

0. 小白导读:Pandas 是什么?先用什么比喻听懂它?

🍚生活化比喻

先听懂:Pandas 就是"会编程的超级 Excel"。你在 Excel 里做的所有事——打开表格、筛选、排序、按分组求和、两表匹配——Pandas 都能做,而且能自动化、能处理几十万行、能写进代码里反复运行

科研里最常用的场景:

  • 从 GEO 下载的表达矩阵(几万行基因 × 几百个样本)→ Pandas 读取、清洗

  • 中药成分-靶点表 → Pandas 筛选、去重、合并

  • 临床数据表 → Pandas 分组统计、透视

一句话:NumPy 管"算",Pandas 管"表"。Pandas 的表格(DataFrame)底层就是 NumPy 数组。

1. Pandas 是什么

Pandas 是 Python 最核心的数据分析库,提供表格型数据(DataFrame)——类似 Excel 表格,但比 Excel 强大得多。

为什么科研必须用 Pandas?

  • 一键读取 CSV / Excel / TSV 等格式
  • 灵活的缺失值处理、数据清洗
  • 按分组计算统计量(groupby 是科研高频操作)
  • 两个表按条件合并(merge,类似 Excel 的 VLOOKUP)
📌要点

2026 年版本说明:pandas 3.0(2026 年 1 月发布)引入了重要变化——Copy-on-Write 默认开启(修改 DataFrame 时不再意外改动原数据);字符串列默认使用 PyArrow 类型。本书代码基于 pandas 3.x,但大多数操作在 pandas 2.x 上也兼容。

python
import pandas as pd
import numpy as np

2. 数据结构:Series 与 DataFrame

2.1 Series(一维带标签数组)

python
# 从列表创建 Series
s = pd.Series([3.2, 5.8, 1.5], index=["丹参", "川芎", "黄芪"])
print(s)
# 丹参    3.2
# 川芎    5.8
# 黄芪    1.5
# dtype: float64

# 访问
print(s["丹参"])      # 3.2
print(s.values)       # [3.2 5.8 1.5]
print(s.index)        # Index(['丹参', '川芎', '黄芪'], dtype='object')

2.2 DataFrame(二维表格,核心)

🍚生活化比喻

先听懂:DataFrame 就是一张带表头的 Excel 表格。每一列是一个"字段"(样本名、分组、浓度),每一行是一条"记录"(一个样本的数据)。和 Excel 不同的是,你可以用代码自动操作它——筛选、排序、合并、计算,全部自动化。

python
# 从字典创建(每个键是一列)
df = pd.DataFrame({
    "sample":  ["S001", "S002", "S003"],
    "group":   ["control", "model", "model"],
    "value":   [3.2, 5.8, 1.5]
})
print(df)
#   sample   group  value
# 0   S001  control    3.2
# 1   S002    model    5.8
# 2   S003    model    1.5

3. 数据读写

3.1 CSV 文件

python
# 读取
df = pd.read_csv("data.csv")
df = pd.read_csv("data.csv", encoding="utf-8")          # 指定编码
df = pd.read_csv("data.csv", index_col=0)               # 指定第0列为行索引
df = pd.read_csv("data.csv", na_values=["NA", "N/A"])   # 指定缺失值标记

# 写入
df.to_csv("output.csv", index=False, encoding="utf-8")  # 不写行索引

3.2 Excel 文件

python
# 读取(需要安装 openpyxl 或 xlrd)
df = pd.read_excel("data.xlsx", sheet_name="Sheet1")

# 写入
df.to_excel("output.xlsx", sheet_name="结果", index=False)

3.3 其他常见格式

python
# TSV(制表符分隔)
pd.read_table("data.tsv", sep="\t")

# 直接读取剪贴板(从 Excel 复制后,Ctrl+C,然后:
df = pd.read_clipboard()

4. 数据查看与描述

python
df = pd.read_csv("data.csv")

df.head()         # 前5行
df.tail()         # 后5行
df.head(10)       # 前10行

df.info()         # 列名、非空计数、数据类型(科研最常用!)
df.describe()     # 数值列的统计摘要(均值、标准差、四分位数)
df.shape          # (行数, 列数)
df.columns        # 列名列表
df.dtypes         # 每列类型
💡提示

拿到数据的第一步:永远先 df.head() + df.info(),了解数据长什么样、有没有缺失值、类型对不对。


5. 选择与过滤

5.1 列选择

python
# 选单列 → Series
df["group"]
df.group

# 选多列 → DataFrame
df[["sample", "value"]]

5.2 行选择

python
# 按位置(行号)
df.iloc[0]          # 第1行
df.iloc[0:3]        # 第1-3行(左闭右开)
df.iloc[[0, 2, 3]]  # 第1、3、4行

# 按标签(行索引名)
df.loc[0]           # 索引为0的行
df.loc[0:2]         # 索引0到2的行(loc 是闭区间!)
注意

iloc左闭右开loc闭区间。这是高频混淆点。

5.3 布尔索引(科研最高频操作)

python
# 筛选 model 组的数据
df[df["group"] == "model"]

# 筛选 value > 3 的数据
df[df["value"] > 3]

# 组合条件(用 & | ~,不是 and or not)
df[(df["group"] == "model") & (df["value"] > 3)]
df[df["group"].isin(["model", "control"])]  # 属于某集合

5.4 query 方法(简洁写法)

python
# 等价于上面的组合条件
df.query("group == 'model' and value > 3")

6. 数据清洗

6.1 缺失值处理

python
# 检测缺失
df.isnull().sum()          # 每列缺失计数
df.isna().sum()            # 同上

# 删除缺失
df.dropna()                # 删除有缺失的行
df.dropna(axis=1)          # 删除有缺失的列
df.dropna(subset=["value"])  # 只在指定列检查缺失

# 填充缺失
df.fillna(0)               # 用0填充
df.fillna(df["value"].mean())  # 用均值填充
df["value"].fillna(method="ffill")  # 用前一个值填充

6.2 重复值处理

python
df.duplicated()                    # 判断重复行
df.drop_duplicates()               # 删除重复行
df.drop_duplicates(subset=["sample"])  # 按指定列去重

6.3 类型转换

python
# 转数值
df["value"] = pd.to_numeric(df["value"], errors="coerce")
# 转日期
df["date"] = pd.to_datetime(df["date"], format="%Y-%m-%d")
# 转字符串
df["sample"] = df["sample"].astype(str)
💡提示

errors="coerce" 表示遇到无法转换的值设为 NaN,比直接报错更友好。

6.4 字符串处理

python
df["gene"].str.strip()              # 去首尾空白
df["gene"].str.replace(" ", "")     # 去掉空格
df["gene"].str.upper()              # 转大写
df["gene"].str.contains("TP53")     # 是否包含
df["gene"].str.split("_")           # 拆分
df["gene"].str.strip().str.upper()  # 链式调用
💡提示

科研中清洗基因名、样本名时,strip + upper + replace 是每日必用组合。


7. 排序与排名

python
# 按值排序
df.sort_values("value")                     # 升序
df.sort_values("value", ascending=False)    # 降序
df.sort_values(["group", "value"], ascending=[True, False])  # 多列排序

# 按索引排序
df.sort_index()

# 排名
df["rank"] = df["value"].rank()             # 默认升序排名
df["rank"] = df["value"].rank(ascending=False)  # 降序排名

8. 分组聚合(科研最高频操作之一)

🍚生活化比喻

先听懂groupby 就像把一堆混在一起的卡片按颜色分类。比如你有 100 只小鼠的数据(对照组 50 只、模型组 50 只),groupby("group") 先把它们分成两堆,然后对每堆分别算均值、标准差——"按组统计"就是科研里最常做的事。

8.1 groupby 基本用法

python
# 按 group 分组,计算每组的均值
df.groupby("group")["value"].mean()

# 分组后求多个统计量
df.groupby("group")["value"].agg(["mean", "std", "count"])

# 分组后多列多统计量
df.groupby("group").agg({
    "value": ["mean", "std"],
    "age": "mean"
})

8.2 transform:保持原行数的分组计算

transform 返回与原 DataFrame 等长的结果,常用于添加一列"每组的均值"

python
# 每行减去该组均值(组内中心化)
df["group_mean"] = df.groupby("group")["value"].transform("mean")
df["centered"] = df["value"] - df["group_mean"]

8.3 apply:自定义分组操作

python
# 对每组应用自定义函数
def zscore_group(x):
    return (x - x.mean()) / x.std()

df["zscore"] = df.groupby("group")["value"].transform(zscore_group)

9. 合并连接

9.1 merge(类似 SQL JOIN 或 Excel VLOOKUP)

🍚生活化比喻

先听懂merge 就像Excel 里的 VLOOKUP(按列匹配)。你有两张表,一张是"基因 → 表达量",另一张是"基因 → 蛋白编号",想按"基因"这一列把两张表拼成一张——merge 就是干这个的。how 参数决定"以谁为主":

  • inner = 只保留两边都有的行(交集)

  • left = 以左边表为主,右边没有的填 NaN

  • outer = 两边都保留(并集)

python
# 两个表按公共列合并
targets = pd.DataFrame({
    "gene":    ["TP53", "AKT1", "EGFR"],
    "uniprot": ["P04637", "P31749", "P00533"]
})
expr = pd.DataFrame({
    "gene": ["TP53", "AKT1", "TP53", "EGFR"],
    "value": [3.2, 5.8, 4.1, 2.5]
})

# 内连接(默认)
merged = pd.merge(expr, targets, on="gene", how="inner")

# 左连接(保留左边所有行)
pd.merge(expr, targets, on="gene", how="left")

# 右连接
pd.merge(expr, targets, on="gene", how="right")

# 外连接(保留两边所有行)
pd.merge(expr, targets, on="gene", how="outer")

9.2 concat(纵向/横向拼接)

python
# 纵向拼接(行方向)
df1 = pd.DataFrame({"A": [1, 2], "B": [3, 4]})
df2 = pd.DataFrame({"A": [5, 6], "B": [7, 8]})
pd.concat([df1, df2], axis=0)      # 默认,追加行

# 横向拼接(列方向)
pd.concat([df1, df2], axis=1)      # 列方向拼接

9.3 join(按索引合并)

python
# 按索引合并两个 DataFrame
df1.join(df2, lsuffix="_left", rsuffix="_right")

10. 透视表与交叉表

10.1 pivot_table(科研高频)

python
# 生成透视表:行为 group,列为 drug,值为 value 的均值
pd.pivot_table(df,
               values="value",
               index="group",
               columns="drug",
               aggfunc="mean",
               fill_value=0)

10.2 crosstab(交叉表,统计频数)

python
# 两个分类变量的频数统计
pd.crosstab(df["group"], df["drug"])

11. 描述统计与相关性

python
# 描述统计
df["value"].mean()           # 均值
df["value"].median()         # 中位数
df["value"].std()            # 标准差
df["value"].min() / .max()   # 最小值 / 最大值
df["value"].quantile([0.25, 0.5, 0.75])  # 四分位数

# 频数统计
df["group"].value_counts()               # 分组频数
df["group"].value_counts(normalize=True)  # 百分比

# 相关性矩阵
df[["value1", "value2", "value3"]].corr()      # Pearson 相关
df[["value1", "value2"]].corr(method="spearman")  # Spearman 相关

12. 补充:科研高频操作

12.1 时间序列处理

python
# 转日期
df["date"] = pd.to_datetime(df["date"])

# 按时间重采样
df.set_index("date")["value"].resample("D").mean()     # 日均值
df.set_index("date")["value"].resample("W").mean()     # 周均值
df.set_index("date")["value"].resample("ME").mean()    # 月均值

# 滚动窗口
df["rolling_mean"] = df["value"].rolling(window=3).mean()  # 3点滑动平均

# 时间差
df["time_diff"] = df["date"].diff()  # 相邻时间差

12.2 apply / map / applymap

python
# apply:对整个 Series 或 DataFrame 应用函数
df["value_sq"] = df["value"].apply(lambda x: x ** 2)

# map:对 Series 每个值做映射
df["group_num"] = df["group"].map({"control": 0, "model": 1})

# 对 DataFrame 每个元素应用函数(不常用)
df.select_dtypes(include="number").applymap(lambda x: round(x, 2))

12.3 大数据分块读取

python
# 分块读取 GB 级文件(节省内存)
chunks = pd.read_csv("big_data.csv", chunksize=10000)
results = []
for chunk in chunks:
    # 处理每块数据
    results.append(chunk.groupby("gene")["value"].mean())
result = pd.concat(results)

12.4 快速绘图

python
import matplotlib.pyplot as plt

df["value"].plot(kind="hist", bins=20)     # 直方图
df.groupby("group")["value"].mean().plot(kind="bar")  # 柱状图
df.plot(kind="scatter", x="value1", y="value2")       # 散点图
plt.show()

# 箱线图
df.boxplot(column="value", by="group")

12.5 数据标准化

python
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
df["value_z"] = scaler.fit_transform(df[["value"]])  # 注意:需要二维输入

13. 常见坑

说明 正确做法
ilocloc 混淆 前者左闭右开,后者闭区间 记清楚:i=位置,l=标签
修改视图触发警告 pandas 3.x 的 Copy-on-Write 显式 .copy() 再修改
链式赋值 df[df.a>0]['b']=1 可能无效 .loc[条件, 列] = 值
读 CSV 中文乱码 默认编码问题 encoding="utf-8"
inplace=True 在 pandas 3.x 中逐步淘汰 df = df.dropna()
忘记 errors="coerce" 类型转换报错 pd.to_numeric(..., errors="coerce")
groupby 后忘记 reset_index 分组键还在索引里 链式 .reset_index()

14. 练习与交付物

交付物(必做)

笔记

科研数据清洗报告:选取公开科研数据(如 GEO 表达矩阵或中药成分-靶点表),完成以下步骤:

  1. 读取:用 pd.read_csv 读取数据,指定编码。
  2. 查看head()info()describe(),记录原始数据概况。
  3. 清洗: - 处理缺失值(fillnadropna) - 删除重复行 - 类型转换(pd.to_numeric) - 字符串清洗(strip / upper / replace
  4. 筛选:用布尔索引筛选出满足条件的数据。
  5. 分组统计:按分组计算均值、标准差。
  6. 输出:清洗前后对比说明 + 干净数据 CSV。

进阶练习(用真实数据练手)

  1. 从 GEO 数据库下载一个表达矩阵 CSV(如 GSE 系列),用 Pandas 读取并做初步探索。
  2. 对两个表做 merge 合并(如基因表达表 + 基因注释表)。
  3. 生成一个透视表(行为样本分组,列为基因名,值为表达量均值)。
  4. groupby + transform 对每组表达式做组内标准化。

15. 速查表

需求 代码
读 CSV pd.read_csv("path", encoding="utf-8")
写 CSV df.to_csv("path", index=False)
快速查看 df.head() / df.info() / df.describe()
选列 df["col"] / df[["col1","col2"]]
选行 df.iloc[0:3] / df.loc[0:2]
布尔筛选 df[df["col"] > 3]
组合条件 df[(条件1) & (条件2)]
缺失处理 df.isnull().sum() / df.fillna(0) / df.dropna()
分组聚合 df.groupby("group")["value"].mean()
分组多统计 df.groupby("group")["value"].agg(["mean","std"])
合并 pd.merge(df1, df2, on="key", how="left")
透视表 pd.pivot_table(df, values="v", index="i", columns="c")
排序 df.sort_values("col", ascending=False)
类型转换 pd.to_numeric(df["col"], errors="coerce")
时间序列 pd.to_datetime(df["date"])

上一章:02 · NumPy 数值计算基础 | 下一章:04 · 包管理与项目版本管理

配套学习资源

共 4 条 · 点击跳转