ScanPy - Preprocessing and clustering 3k PBMCs (legacy workflow)工作复现

话不多说先上官网:

  1. ScanPy官网:Preprocessing and clustering 3k PBMCs (legacy workflow)
  2. 官方数据集下载:pbmc3k_filtered_gene_bc_matrices.tar.gz
  3. 如果使用R语言的话,可以参考我写的另一篇:Seurat - Guided Clustering Tutorial官方文档学习及复现区别就是本文是Python代码复现(借鉴的Seurat),Seurat是R语言。

由于之前已经对Seurat中的10x Genomics数据集的教程进行了复现,所以这篇文档只是参考官方教程来进行撰写的,会加入自己的理解以及教程中没有涉及到的扩展的内容,本文的代码会使用大模型来添加注释,每段代码都会加注释方便学习和理解。


1 ScanPy和Seurat的故事

看到ScanPy官网上第一句话是:In May 2017, this started out as a demonstration that Scanpy would allow to reproduce most of Seurat's guided clustering tutorial (Satija et al., 2015).We gratefully acknowledge Seurat's authors for the tutorial! In the meanwhile, we have added and removed a few pieces. 感觉还是挺有意思的,就去了解了一下,写个小前言。

Seurat先行:Seurat于2015年发布,是较早的单细胞RNA测序分析平台之一。

Scanpy借鉴:Scanpy在2017年开发,其设计受到Seurat的启发,官网提到Scanpy可复制Seurat的大部分引导式聚类教程,表明Scanpy在功能和流程设计上借鉴了Seurat的一些思路和方法 。

Seurat适用于R语言编程环境,能与R的其他生物信息学包集成。Scanpy适用于Python编程环境,能与Python的科学计算和机器学习库协同工作,适合更灵活的数据分析和可视化需求。

功能实现与算法上的对比

  • SNN图生成:两软件在SNN图的生成上存在差异,Seurat在默认情况下比Scanpy给出更多的高连接SNN图。
  • 聚类差异:使用默认设置时,两软件的Louvain聚类结果不同,但在Leiden算法实现上相同。
  • 差异表达分析(DEA)显著性差异基因 :Seurat的显著性marker基因比Scanpy多约50%,这主要是由于两软件在Wilcoxon功能的实现上有所不同,Seurat需要tie校正,而Scanpy默认省略;同时,两软件在调整p值的方法上也不同,Seurat采用Bonferroni multiple testing correction,而Scanpy采用Benjamini-Hochberg多重测试校正。过滤机制:Seurat在执行Wilcoxon秩和检验之前会进行过滤,而Scanpy在不调用其他函数的情况下不会进行这种过滤。

2 Preparation

2.1 import data and packages

首先准备工作就是引入数据集,安装对应的包。

官方数据集下载:pbmc3k_filtered_gene_bc_matrices.tar.gz

装包,官方文档:https://scanpy.readthedocs.io/en/stable/installation.html

bash 复制代码
$ pip install scanpy
python 复制代码
# 导库
import numpy as np  # 数值计算
import pandas as pd  # 数据处理
import scanpy as sc  # Scanpy单细胞RNA测序数据分析工具包

# 设置日志的详细程度,0表示只显示错误,1显示警告,2显示信息,3显示提示
sc.settings.verbosity = 3

# 打印Scanpy包的头信息,包括版本号等信息
sc.logging.print_header()

# 设置绘图参数,设置图片的分辨率(dpi)为80,并将背景颜色设置为白色
sc.settings.set_figure_params(dpi=80, facecolor='white')

# 指定用于存储分析结果文件的路径,结果将写入到'write/pbmc3k.h5ad'文件中
results_file = 'write/pbmc3k.h5ad'

# 从指定的目录读取10x Genomics格式的数据文件
# 参数./filtered_gene_bc_matrices/hg19/是存放MTX文件的目录
# 参数var_names='gene_symbols'表示使用基因符号作为变量名
# 参数cache=True表示缓存文件,以便以后更快地读取
adata = sc.read_10x_mtx(
    './filtered_gene_bc_matrices/hg19/',  # 数据文件所在目录
    var_names='gene_symbols',  # 变量名使用基因符号
    cache=True)  # 缓存文件,以便后续快速加载

2.2 pre - processing

python 复制代码
# 绘制单细胞RNA测序数据中最高表达的基因的表达分布图。
sc.pl.highest_expr_genes(adata, n_top=20)
python 复制代码
# 过滤低质量细胞样本
# filtered out 19024 genes that are detected in less than 3 cells
sc.pp.filter_cells(adata, min_genes=200)
sc.pp.filter_genes(adata, min_cells=3)

High proportions are indicative of poor-quality cells (Islam et al. 2014; Ilicic et al. 2016), possibly because of loss of cytoplasmic RNA from perforated cells. The reasoning is that mitochondria are larger than individual transcript molecules and less likely to escape through tears in the cell membrane.

高比例表明细胞质量差(Islam 等人,2014 年;Ilicic 等人,2016 年),可能是因为穿孔细胞的细胞质 RNA 丢失。原因是线粒体比单个转录物分子大,不太可能通过细胞膜中的撕裂逃逸。

使用 ,我们可以非常高效地计算许多指标。pp.calculate_qc_metrics

其实就是说,线粒体基因表达比例过高可能是因为细胞受损,导致细胞质中的RNA丢失,而线粒体基因相对保留得更多。我们可以用 pp.calculate_qc_metrics 这个工具来计算相关指标,从而判断细胞的质量。

python 复制代码
# 将线粒体基因标记为 'mt'
# 创建一个新的列 'mt',用于标记线粒体基因。
# 如果基因名以 "MT-" 开头,则认为它是线粒体基因,标记为 True;否则标记为 False。
adata.var["mt"] = adata.var_names.str.startswith("MT-")

# 计算质量控制(QC)指标
sc.pp.calculate_qc_metrics(
    adata,                          # 输入的 AnnData 对象
    qc_vars=["mt"],                 # 指定需要计算的 QC 指标变量,这里计算线粒体基因(mt)相关的指标
    percent_top=None,               # 不计算最高表达基因的百分比(默认值)
    log1p=False,                    # 不对结果取 log1p 变换(默认值)
    inplace=True                    # 将计算结果直接存储在 adata 对象中
)
python 复制代码
sc.pl.violin(
    adata,
    ["n_genes_by_counts", "total_counts", "pct_counts_mt"],
    jitter=0.4,
    multi_panel=True,
)

基因的数量,每个细胞包含的表达量,线粒体基因表达量的百分比绘图如下:

去除表达线粒体基因过多或总计数过多的细胞:

python 复制代码
import matplotlib.pyplot as plt

fig, axes = plt.subplots(1, 2, figsize=(12, 5))  
# 在第一个子图中绘制总表达量与线粒体基因比例的关系
sc.pl.scatter(adata, x="total_counts", y="pct_counts_mt", ax=axes[0], show=False)  
axes[0].set_title("Total Counts vs. Percent Mitochondrial Counts")  
# 在第二个子图中绘制总表达量与检测到的基因数的关系
sc.pl.scatter(adata, x="total_counts", y="n_genes_by_counts", ax=axes[1], show=False)  
axes[1].set_title("Total Counts vs. Number of Genes by Counts") 
plt.tight_layout()
plt.show()

3 Slicing highly-variable genes

python 复制代码
# 筛选检测到的基因数小于2500的细胞
# adata.obs.n_genes_by_counts 是一个包含每个细胞检测到的基因数的列
# 这里我们保留基因数小于2500的细胞,排除基因数过多的异常细胞
adata = adata[adata.obs.n_genes_by_counts < 2500, :]

# 筛选线粒体基因比例小于5%的细胞
# adata.obs.pct_counts_mt 是一个包含每个细胞线粒体基因表达比例的列
# 这里我们保留线粒体基因比例小于5%的细胞,排除可能受损的细胞
adata = adata[adata.obs.pct_counts_mt < 5, :].copy()
python 复制代码
# 对数据进行总和归一化,使每个细胞的总表达量(UMI数)标准化为目标值
sc.pp.normalize_total(adata, target_sum=1e4)

normalizing counts per cell

finished (0:00:00)

python 复制代码
# 做对数
sc.pp.log1p(adata)
python 复制代码
# 寻找高变基因,指在不同细胞中表达水平差异较大的基因,这些基因通常在后续分析中更具生物学意义
sc.pp.highly_variable_genes(adata, min_mean=0.0125, max_mean=3, min_disp=0.5)
# 画图
sc.pl.highly_variable_genes(adata)

extracting highly variable genes

finished (0:00:00)

--> added

'highly_variable', boolean vector (adata.var)

'means', float vector (adata.var)

'dispersions', float vector (adata.var)

'dispersions_norm', float vector (adata.var)


4 Obtain datasets with only high-variable genes

python 复制代码
# 仅保留高变基因
adata = adata[:, adata.var.highly_variable]
# adata.var.highly_variable 是一个布尔数组,表示每个基因是否为高变基因。

# 使用线性回归去除总表达量和线粒体基因比例的影响
sc.pp.regress_out(adata, ["total_counts", "pct_counts_mt"])
# 1. total_counts:每个细胞的总表达量(UMI数)。
# 2. pct_counts_mt:每个细胞的线粒体基因表达比例。

# 对数据进行标准化(Z-score标准化),并将值限制在 [-10, 10] 范围内
sc.pp.scale(adata, max_value=10)
# max_value=10 参数限制了标准化后的值范围,避免极端值对后续分析(如降维)产生过大影响。
# 标准化后的数据更适合用于主成分分析 PCA

5 Principal component analysis (PCA)

用PCA来降维,为什么?(思考一下)

python 复制代码
# 对数据进行PCA降维
sc.tl.pca(adata, svd_solver="arpack")

在 PCA 坐标中制作散点图,但之后用不到

python 复制代码
sc.pl.pca(adata, color="CST3")

检查一下单个 PC 对数据总方差的贡献。这为我们提供了有关我们应该考虑多少台 PC 的信息,以便计算单元的邻域关系,例如用于聚类函数或 tSNE 。根据我们的经验,通常粗略估计 PC 的数量就可以了。

python 复制代码
sc.pl.pca_variance_ratio(adata, log=True)


保存结果

python 复制代码
adata.write(results_file)
adata
bash 复制代码
AnnData object with n_obs × n_vars = 2638 × 1838
    obs: 'n_genes', 'n_genes_by_counts', 'total_counts', 'total_counts_mt', 'pct_counts_mt'
    var: 'gene_ids', 'n_cells', 'mt', 'n_cells_by_counts', 'mean_counts', 'pct_dropout_by_counts', 'total_counts', 'highly_variable', 'means', 'dispersions', 'dispersions_norm', 'mean', 'std'
    uns: 'log1p', 'hvg', 'pca'
    obsm: 'X_pca'
    varm: 'PCs'

6 Computing the neighborhood graph

为了重现 Seurat 的结果,官网取以下值:

python 复制代码
sc.pp.neighbors(adata, n_neighbors=10, n_pcs=40)
bash 复制代码
computing neighbors
    using 'X_pca' with n_pcs = 40
    finished: added to `.uns['neighbors']`
    `.obsp['distances']`, distances for each pair of neighbors
    `.obsp['connectivities']`, weighted adjacency matrix (0:00:22)

7 Embedding the neighborhood graph

建议使用 UMAP 将图形嵌入两个维度,它可能比 tSNE 更忠实于流形的全局连接性,即它更好地保留了轨迹。

python 复制代码
sc.tl.umap(adata)
sc.pl.umap(adata, color=["CST3", "NKG7", "PPBP"])

前面的图显示了 "原始" (归一化、对数化但未校正) 基因表达,还可以通过 use_raw = False来绘制缩放和校正的基因表达。

python 复制代码
sc.pl.umap(adata, color=["CST3", "NKG7", "PPBP"], use_raw=False)

8 Clustering the neighborhood graph

scanPy里面推荐使用leiden来绘制,首先你要先安装igraph(pip3)

bash 复制代码
pip3 install igraph
bash 复制代码
conda install -c conda-forge python-igraph
python 复制代码
sc.tl.leiden(
    adata,
    resolution=0.9,
    random_state=0,
    flavor="igraph",
    n_iterations=2,
    directed=False,
)

绘制聚类

python 复制代码
sc.pl.umap(adata, color=["leiden", "CST3", "NKG7"])


保存结果

python 复制代码
adata.write(results_file)

9 Finding marker genes


10 思考

10.1 什么是PCA降维?

PCA用于将高维数据转换为低维数据,同时尽可能保留原始数据中的主要变异信息。简单来说,PCA通过以下步骤实现降维:

  • 寻找主成分:PCA通过计算数据的协方差矩阵,找到一组正交的"主成分"(即新的坐标轴)。这些主成分是数据中变异最大的方向。
  • 投影数据:将原始数据投影到这些主成分上,得到新的低维表示。主成分的数量通常远小于原始数据的维度。
  • 保留主要变异:通过选择前几个主成分(通常解释了大部分数据的变异),可以将数据从高维空间压缩到低维空间,同时保留最重要的信息。

数据集是几维的?

在单细胞RNA测序数据中,每个细胞的基因表达值构成了数据的一个维度。假设你的数据集中有 n 个细胞m 个基因 ,那么数据集的维度可以理解为 m,其中每一维代表一个基因的表达值。

例如:如果数据集中有 10,000 个基因,那么数据集就是 10,000 维的。每个细胞是一个 10,000 维的向量,表示其在每个基因上的表达值。

10.2 为什么要降维?

单细胞RNA测序数据通常是高维的(基因数可能达到数千甚至数万),但实际的生物学信息可能集中在少数几个维度上。高维数据存在以下问题:

  • 计算复杂度高:高维数据的分析(如聚类、分类等)计算成本很高。
  • 噪声干扰:高维数据中可能包含大量噪声,掩盖了真正的生物学信号。
  • "维度灾难":在高维空间中,数据点之间的距离变得不明显,难以进行有效的分析。

因此,降维的目的是:

  • 降低计算成本:将数据从高维空间压缩到低维空间,减少计算复杂度。
  • 去除噪声:通过保留主要变异信息,去除无关的噪声。
  • 便于可视化:低维数据(如二维或三维)可以更容易地进行可视化和解释。
  • 提高分析效率:低维数据更适合进行后续的聚类、分类、轨迹推断等分析。
python 复制代码
# 对数据进行PCA降维
sc.tl.pca(adata, svd_solver="arpack")

svd_solver="arpack" :指定用于计算奇异值分解(SVD)的求解器。arpack 是一种高效的稀疏矩阵求解器,适用于大规模数据。

运行该代码后,PCA 的结果会被存储在 adata.obsm['X_pca'] 中,这是一个低维矩阵,每一行代表一个细胞在主成分空间中的坐标。通常,前几个主成分(如前 20 个)会被用于后续的分析。

  • PCA降维:将高维数据转换为低维数据,保留主要变异信息。
  • 数据集维度 :单细胞数据通常是基因数(m)维的。
  • 降维原因:降低计算成本、去除噪声、便于可视化和提高分析效率。
相关推荐
NoviceLearningRecord23 分钟前
解决webdriver和Chrome不匹配的办法
前端·chrome·python
max50060035 分钟前
用python 的 sentiment intensity analyzer的情感分析器,将用户评论进行分类
人工智能·python·分类
性感博主在线瞎搞37 分钟前
【神经网络】python实现神经网络(一)——数据集获取
人工智能·python·深度学习·神经网络·机器学习·手写数字识别
not 程序员1 小时前
cmd中有cl但是conda虚拟环境没用cl
开发语言·python·conda
Linux运维老纪1 小时前
Python实战项目(‌Hands-on Python Project)
开发语言·数据库·python·sql·mysql·云计算·运维开发
白杨攻城狮1 小时前
.net 与 Pythonnet库的使用心得
python·c#·.net
Bruce_Liuxiaowei2 小时前
用Python实现PDF转Doc格式小程序
python·小程序·pdf
愚公搬代码3 小时前
【愚公系列】《Python网络爬虫从入门到精通》045-Charles的SSL证书的安装
网络·爬虫·python·网络协议·ssl
邹霍梁@开源软件GoodERP3 小时前
【DuodooTEKr】物联DTU设备与Odoo18 Maintenance设备模块IOT模块集成技术方案
人工智能·python·物联网·开源·制造