前两篇我们分别学习了 Python 基础和 Python 进阶语法。这一篇专门讲 pandas 数据框 DataFrame 的操作。这篇只聚焦 pandas 本身,不展开 AnnData,也不系统讲 metadata 整理。因为后面介绍 AnnData、单样本和多样本数据处理时,adata.obs、adata.var、样本信息表和分组信息表,本质上都会大量用到 pandas DataFrame。
一、为什么单细胞分析要学 pandas?
在 Python 单细胞分析中,pandas 是非常重要的基础工具。
后面你会频繁看到:
它们看起来像普通表格,本质上就是 pandas DataFrame。
因此,在正式学习 AnnData 之前,先掌握 pandas 的常见操作会非常有帮助。
这一篇主要包括:
1. 创建 DataFrame2. 查看 DataFrame3. 读取 csv / tsv4. 行名和列名5. 提取行和列6. 条件筛选7. 增加、修改和删除8. 排序9. 分组统计10. 合并数据框
第一部分:创建 DataFrame
二、导入 pandas
使用 pandas 前,需要先导入:
通常约定把 pandas 简写成 pd。
三、用字典创建 DataFrame
先创建一个学生成绩表,方便理解数据框的基本操作。
import pandas as pddf = pd.DataFrame({ ”姓名”: [”李一”, ”燕三”, ”赵四”], ”语文”: [92, 82, 77], ”数学”: [98, 89, 92], ”英语”: [100, 92, 72]})df
结果:
姓名 语文 数学 英语0 李一 92 98 1001 燕三 82 89 922 赵四 77 92 72
这里可以理解为:
字典的 key 变成列名字典的 value 变成每一列的数据
四、把姓名作为行名
很多时候,我们希望姓名不是普通列,而是行名。
df = pd.DataFrame({ ”语文”: [92, 82, 77], ”数学”: [98, 89, 92], ”英语”: [100, 92, 72]}, index=[”李一”, ”燕三”, ”赵四”])df
结果:
语文 数学 英语李一 92 98 100燕三 82 89 92赵四 77 92 72
这里的 index 就是行名。
在单细胞分析里,barcode 通常也会作为行名。
第二部分:查看 DataFrame
五、查看前几行
默认查看前 5 行。
如果想查看前 2 行:
六、查看后几行
如果想查看后 2 行:
七、查看行列数
输出类似:
意思是:
八、查看列名和行名
查看列名:
查看行名:
查看数据类型:
第三部分:读取 csv 和 tsv 文件
九、读取 csv 文件
df = pd.read_csv(”data.csv”)
如果第一列是行名:
df = pd.read_csv(”data.csv”, index_col=0)
十、读取 tsv 文件
pandas 里面没有 pd.read_tsv()。
读取 tsv 要用:
df = pd.read_csv(”data.tsv”, sep=”\t”)
如果第一列是行名:
df = pd.read_csv(”data.tsv”, sep=”\t”, index_col=0)
十一、index_col=0 是什么意思?
意思是:
把第 0 列,也就是第一列,作为 DataFrame 的行名。
例如 tsv 文件长这样:
barcode in_tissue array_row array_colAAACAAGTATCTCCCA-1 1 50 102AAACAATCTACTAGCA-1 1 3 43
如果不加 index_col=0,barcode 会变成普通列。
如果加上:
df = pd.read_csv(”qc_metadata.tsv”, sep=”\t”, index_col=0)
那么 barcode 就会成为行名。
第四部分:提取列
十二、提取一列
结果是一个 Series:
李一 92燕三 82赵四 77Name: 语文, dtype: int64
十三、提取多列
结果仍然是 DataFrame:
语文 数学李一 92 98燕三 82 89赵四 77 92
注意:
是一列,返回 Series。
也是一列,但返回 DataFrame。
第五部分:提取行
十四、使用 loc 按行名提取
提取李一这一行。
如果提取多行:
结果:
语文 数学 英语李一 92 98 100赵四 77 92 72
loc 是按名字提取。
十五、使用 iloc 按位置提取
提取第 0 行,也就是第一行。
提取前两行:
注意 Python 切片左闭右开:
iloc 是按位置提取。
十六、loc 和 iloc 的区别
例如:
表示取李一的语文成绩。
表示取第 0 行、第 0 列的值。
第六部分:条件筛选
十七、筛选数学成绩大于 90 的学生
df2 = df[df[”数学”] > 90]df2
结果:
语文 数学 英语李一 92 98 100赵四 77 92 72
这里的逻辑是:
先生成一组 True / False:
李一 True燕三 False赵四 TrueName: 数学, dtype: bool
然后用这组 True / False 筛选数据框。
十八、多条件筛选
例如筛选数学大于 90,并且英语大于 80 的学生:
df3 = df[ (df[”数学”] > 90) & (df[”英语”] > 80)]df3
在 pandas 里面:
and 要写成 &or 要写成 |not 要写成 ~
而且每个条件都要用小括号包起来。
错误写法:
df[(df[”数学”] > 90) and (df[”英语”] > 80)]
正确写法:
df[(df[”数学”] > 90) & (df[”英语”] > 80)]
十九、根据行名筛选
如果想提取王五和赵四:
如果想判断行名是否属于某几个值:
df[df.index.isin([”王五”, ”赵四”])]
区别是:
df.loc[[”王五”, ”赵四”]] 按指定顺序提取df[df.index.isin(...)] 按原数据框顺序筛选
第七部分:增加、修改和删除
二十、增加一列
df[”总分”] = df[”语文”] + df[”数学”] + df[”英语”]df
结果:
语文 数学 英语 总分李一 92 98 100 290燕三 82 89 92 263赵四 77 92 72 241
二十一、增加一行
最简单的方法是用 loc。
df.loc[”王五”] = [65, 66, 67, 198]df
如果当前数据框没有 总分 这一列,则写:
df.loc[”王五”] = [65, 66, 67]
二十二、用 concat 合并新增行
新版 pandas 中不推荐再使用 append(),更推荐 pd.concat()。
student_new = pd.DataFrame({ ”语文”: [65], ”数学”: [66], ”英语”: [67]}, index=[”王五”])df2 = pd.concat([df, student_new])df2
注意,新建 DataFrame 时,字典应该这样写:
pd.DataFrame({ ”语文”: [65], ”数学”: [66], ”英语”: [67]}, index=[”王五”])
不要写成多个字典:
pd.DataFrame({”语文”: ”65”}, {”数学”: ”66”}, {”英语”: ”67”}, index=[”王五”])
这种写法会报错。
二十三、修改某个值
例如修改王五的数学成绩:
df2.loc[”王五”, ”数学”] = 90df2
二十四、删除一列
删除 总分 这一列:
df2 = df2.drop(columns=[”总分”])
如果没有 总分 这一列,就不要执行这句。
二十五、删除一行
删除王五这一行:
df2 = df2.drop(index=[”王五”])
第八部分:排序
二十六、按数学成绩排序
升序:
降序:
df.sort_values(”数学”, ascending=False)
二十七、按行名排序
第九部分:统计和分组
二十八、value_counts:统计每个值出现几次
例如统计每个语文成绩出现几次:
如果想按分数排序:
df[”语文”].value_counts().sort_index()
二十九、groupby:分组统计
例如按语文成绩分组,统计每组人数:
df_yuwen = df.groupby(”语文”)df_yuwen.size()
结果类似:
语文65 177 182 192 2dtype: int64
这里左边的 65、77、82、92 是分组后的 index。右边的数字是每个分数出现的次数。
如果想变成普通数据框:
df_count = df.groupby(”语文”).size().reset_index(name=”人数”)df_count
结果:
语文 人数0 65 11 77 12 82 13 92 2
三十、按组计算平均值
例如按 cluster 计算平均 QC 指标:
qc_df.groupby(”clusters”)[[”n_genes_by_counts”, ”total_counts”]].mean()
虽然这里用了类似单细胞 QC 表的列名,但本质上仍然只是 pandas 的分组统计操作。
第十部分:合并数据框
三十一、concat:上下拼接
df_all = pd.concat([df1, df2])
适合把两个行结构相同的数据框上下合并。
三十二、concat:左右拼接
df_all = pd.concat([df1, df2], axis=1)
axis=1 表示按列拼接。
三十三、merge:按共同列合并
例如一个表是成绩,一个表是班级信息:
score_df = pd.DataFrame({ ”姓名”: [”李一”, ”燕三”, ”赵四”], ”语文”: [92, 82, 77]})class_df = pd.DataFrame({ ”姓名”: [”李一”, ”燕三”, ”赵四”], ”班级”: [”一班”, ”一班”, ”二班”]})
按姓名合并:
merged_df = pd.merge(score_df, class_df, on=”姓名”)merged_df
结果:
姓名 语文 班级0 李一 92 一班1 燕三 82 一班2 赵四 77 二班
第十一部分:常见错误
1. pandas 没有 read_tsv
错误:
df = pd.read_tsv(”data.tsv”)
正确:
df = pd.read_csv(”data.tsv”, sep=”\t”)
2. 筛选 df2 时不要用 df 的条件
错误:
正确:
df5 = df2[df2[”数学”] > 90]
筛选哪个数据框,条件也尽量来自同一个数据框。
3. 多行行名筛选不能直接用 == 列表
错误:
df6 = df2[df2.index == [”王五”, ”赵四”]]
正确:
df6 = df2.loc[[”王五”, ”赵四”]]
或者:
df6 = df2[df2.index.isin([”王五”, ”赵四”])]
4. 增加行时 DataFrame 写法错误
错误:
student_new = pd.DataFrame({”语文”: ”65”}, {”数学”: ”66”}, {”英语”: ”67”}, index=[”王五”])
正确:
student_new = pd.DataFrame({ ”语文”: [65], ”数学”: [66], ”英语”: [67]}, index=[”王五”])
本篇小结
这一篇专门介绍了 pandas 数据框的基础操作:
1. 创建 DataFrame2. 查看 DataFrame3. 读取 csv / tsv 文件4. index_col=0 的含义5. 提取行和列6. loc / iloc7. 条件筛选8. 增加、修改和删除9. 排序10. value_counts 和 groupby11. concat 和 merge
这一篇先只讲 pandas 数据框本身,不展开 AnnData,也不讲复杂 metadata 整理。
下一篇可以正式进入:
AnnData 数据结构:认识 adata.X、adata.obs、adata.var、adata.uns、adata.obsm 和 adata.layers。