学习资料来源:
- scanpy主页:https://scanpy.readthedocs.io/en/stable/
- 官网:https://scanpy-tutorials.readthedocs.io/en/latest/pbmc3k.html【注意教程有两个版本,这里是latest版本的学习笔记】
00 环境准备
安装教程:https://scanpy.readthedocs.io/en/stable/installation.html
我这里安装的版本为:scanpy 1.9.1 是最新版
# linux环境,使用conda安装软件
conda create -y -n scanpy python=3
conda activate scanpy
conda install seaborn scikit-learn statsmodels numba pytables -y
conda install -c conda-forge python-igraph leidenalg -y
pip install scanpy
scanpy可以进行:单细胞数据聚类,可视化,轨迹分析,数据整合,空间数据分析等
本次学习使用scanpy进行单细胞数据基础分析:分群聚类注释
01 数据准备
数据来自10x官网:The data consist of 3k PBMCs from a Healthy Donor and are freely available from 10x Genomics
主要的分析结果过程与Seurat官网的教程:https://satijalab.org/seurat/articles/pbmc3k_tutorial.html 基本一致,两个软件的结果也可以进行一下对比
下载链接:https://support.10xgenomics.com/single-cell-gene-expression/datasets/1.1.0/pbmc3k
Note:注意数据下载链接,官网也有不同的数据版本,数据版本为datasets/1.1.0/pbmc3k
# 官网使用的wget下载数据,我们这里改用axel多线程命令下载数据
mkdir -p data/pbmc3k
cd data/pbmc3k
axel -n 100 http://cf.10xgenomics.com/samples/cell-exp/1.1.0/pbmc3k/pbmc3k_filtered_gene_bc_matrices.tar.gz
# 解压数据
tar -xzf pbmc3k_filtered_gene_bc_matrices.tar.gz
数据为10X上游cellranger的标准输出结果:
02 数据读取
在python交互环境中运行:
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import pandas as pd
import scanpy as sc
import seaborn as sns
# verbosity: errors (0), warnings (1), info (2), hints (3)
sc.settings.verbosity = 3
sc.logging.print_header()
#sc.settings.set_figure_params(dpi=80, facecolor='white')
adata = sc.read_10x_mtx('data/pbmc3k/filtered_gene_bc_matrices/hg19/',
var_names='gene_symbols',
cache=True)
# this is unnecessary if using `var_names='gene_ids'` in `sc.read_10x_mtx`
adata.var_names_make_unique()
adata
到此,生成了一个anndata对象,包括2700个细胞,32738基因
AnnData object with n_obs × n_vars = 2700 × 32738
var: 'gene_ids'
对象结构剖析可以参考官方说明:https://anndata.readthedocs.io/en/latest/
03 数据预处理
显示那些在所有细胞中,每个单细胞中产生最高Counts的基因
sc.pl.highest_expr_genes(adata, n_top=20,)
plt.savefig("./01-highest_expr_genes.png")
数据过滤:
- 细胞:空的液滴过滤,每个细胞中至少有200个基因表达
- 基因:低表达基因过滤,每个基因至少在三个细胞中表达
sc.pp.filter_cells(adata, min_genes=200)
sc.pp.filter_genes(adata, min_cells=3)
过滤后的结果:h还剩下2700个细胞 × 13714个基因
adata
AnnData object with n_obs × n_vars = 2700 × 13714
obs: 'n_genes'
var: 'gene_ids', 'n_cells'
关于线粒体基因:
线粒体基因含量高的问题(Lun, McCarthy & Marioni, 2017):
高比例表明细胞质量差(Islam et al. 2014; Ilicic et al. 2016) ,可能是因为穿孔细胞失去了细胞质 RNA,而线粒体比单个转录分子大,不太可能通过细胞膜上的裂口逃脱。
# 让我们收集一些关于线粒体基因的信息,这对质量控制很重要
# annotate the group of mitochondrial genes as 'mt'
adata.var['mt'] = adata.var_names.str.startswith('MT-')
sc.pp.calculate_qc_metrics(adata, qc_vars=['mt'], percent_top=None, log1p=False, inplace=True)
查看数据变化:
adata.var
gene_ids n_cells mt n_cells_by_counts mean_counts pct_dropout_by_counts total_counts
AL627309.1 ENSG00000237683 9 False 9 0.003333 99.666667 9.0
AP006222.2 ENSG00000228463 3 False 3 0.001111 99.888889 3.0
RP11-206L10.2 ENSG00000228327 5 False 5 0.001852 99.814815 5.0
RP11-206L10.9 ENSG00000237491 3 False 3 0.001111 99.888889 3.0
LINC00115 ENSG00000225880 18 False 18 0.006667 99.333333 18.0
... ... ... ... ... ... ... ...
AC145212.1 ENSG00000215750 16 False 16 0.006667 99.407407 18.0
AL592183.1 ENSG00000220023 323 False 323 0.134815 88.037037 364.0
AL354822.1 ENSG00000215615 8 False 8 0.002963 99.703704 8.0
PNRC2-1 ENSG00000215700 110 False 110 0.042963 95.925926 116.0
SRSF10-1 ENSG00000215699 69 False 69 0.025926 97.444444 70.0
[13714 rows x 7 columns]
小提琴绘制三幅质控图:
- 每个细胞中表达的基因个数
- 每个细胞中的总counts数
- 每个细胞中线粒体基因表达的比例
# 画图
outdir = "./"
sc.pl.violin(adata, ['n_genes_by_counts', 'total_counts', 'pct_counts_mt'],
jitter=0.4, multi_panel=True)
plt.savefig(outdir + './02-violin_qc.jpg')
可以看到大多数细胞表达的基因个数在1000以下接近800个左右,线粒体百分比在5%以下:
线粒体基因过滤:
# 去除那些线粒体基因表达过多或总数过多的细胞
sc.pl.scatter(adata, x='total_counts', y='pct_counts_mt')
plt.savefig(outdir + './03-pct_counts_mt.jpg')
sc.pl.scatter(adata, x='total_counts', y='n_genes_by_counts')
plt.savefig(outdir + './03-n_genes_by_counts.jpg')
## 过滤:线粒体百分比<5%,基因个数<2500
adata = adata[adata.obs.n_genes_by_counts < 2500, :]
adata = adata[adata.obs.pct_counts_mt < 5, :]
adata
View of AnnData object with n_obs × n_vars = 2638 × 13714
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'
数据标准化:
# Total-count normalize (library-size correct) the data matrix X to 10,000 reads per cell
sc.pp.normalize_total(adata, target_sum=1e4)
sc.pp.log1p(adata)
高变基因鉴定:
# Identify highly-variable genes
sc.pp.highly_variable_genes(adata, min_mean=0.0125, max_mean=3, min_disp=0.5)
sc.pl.highly_variable_genes(adata)
plt.savefig(outdir + './04-highly_variable_genes.jpg')
数据保存:
adata.raw = adata
# regress_out掉total_counts与mt的影响
sc.pp.regress_out(adata, ['total_counts', 'pct_counts_mt'])
数据归一化
通过主成分分析(PCA)对数据进行降维,揭示数据的主要变化轴,对数据进行降噪处理
# Scale each gene to unit variance. Clip values exceeding standard deviation 10
sc.pp.scale(adata, max_value=10)
04 PCA分析
通过运行主成分分析分析(PCA)来降低数据的维数,它揭示了变化的主轴,并去除了数据的噪音。
# Reduce the dimensionality of the data
sc.tl.pca(adata, svd_solver='arpack')
sc.pl.pca(adata, color='CST3')
plt.savefig(outdir + './05-PCA.jpg')
# PC可视化
sc.pl.pca_variance_ratio(adata, log=True)
plt.savefig(outdir + './05-pca_variance.jpg')
# 保存结果
adata.write(outdir + 'pbmc3k.h5ad')
PC贡献度:
05 聚类
构建neighborhood graph,在这里推荐使用UMAP进行聚类可视化:We suggest embedding the graph in two dimensions using UMAP (McInnes et al., 2018)。
- 它可能比tSNE更忠实于流形的全局连通性,也就是说,它能更好地保存轨迹。
- 在某些情况下,可能仍然会观察到断开连接的clusters和类似的连接冲突。
## 计算KNN Graph
sc.pp.neighbors(adata, n_neighbors=10, n_pcs=40)
# Embedding the neighborhood graph
sc.tl.umap(adata)
sc.pl.umap(adata, color=['CST3', 'NKG7', 'PPBP'])
plt.savefig(outdir + './06-umap1.jpg')
sc.pl.umap(adata, color=['CST3', 'NKG7', 'PPBP'], use_raw=False)
plt.savefig(outdir + './06-umap2.jpg')
降维聚类可视化:
与Seurat和许多其他框架一样,我们推荐Traag et al. (2018)提出的Leiden graph-clustering method(基于优化模块化的社区检测)。注意Leiden直接聚类细胞的邻域图,我们已经在前一节计算过了:
## Clustering the neighborhood graph
# resolution默认为1,这里聚类数可能跟官网会有点不一样
sc.tl.leiden(adata,resolution=0.9)
sc.pl.umap(adata, color=['leiden', 'CST3', 'NKG7'])
plt.savefig(outdir + './06-umap3.jpg',dpi=800,bbox_inches = 'tight')
## save
adata.write(outdir + 'pbmc3k.h5ad')
聚类结果:这里聚成了9类,官网是8类:
06 鉴定markers
让我们计算每个cluster中差异较大的基因的排名。为此,默认情况下,使用AnnData的.raw属性以防它之前被初始化。最简单、最快的方法是t检验:
# Finding marker genes
# t-test
sc.tl.rank_genes_groups(adata, 'leiden', method='t-test')
sc.pl.rank_genes_groups(adata, n_genes=25, sharey=False)
plt.savefig(outdir + './07-rank_genes1.jpg',dpi=800,bbox_inches = 'tight')
t检验:
也可以使用秩和检验wilcoxon: Wilcoxon rank-sum (Mann-Whitney-U)
We recommend using the latter in publications, see e.g., Sonison & Robinson (2018)。
# wilcoxon
sc.tl.rank_genes_groups(adata, 'leiden', method='wilcoxon')
sc.pl.rank_genes_groups(adata, n_genes=25, sharey=False)
plt.savefig(outdir + './07-rank_genes2.jpg')
## save
adata.write(outdir + 'pbmc3k.h5ad')
wilcoxon:
当然,还可以考虑更强大的差异测试包,比如 MAST, limma, DESeq2 and, 对于 python,还有最近的 affxpy
还有另一种选择,用逻辑回归对基因进行排序。例如,这是由Natranos et al. (2018)提出的。本质的区别是,这里,我们使用多变量评估,而传统的差分检验是单变量,Clark et al. (2014)提供更多的细节。
# using logistic regression
sc.tl.rank_genes_groups(adata, 'leiden', method='logreg')
sc.pl.rank_genes_groups(adata, n_genes=25, sharey=False)
plt.savefig(outdir + './07-rank_genes3.jpg')
logreg:
除IL7R仅通过t检验和FCER1A仅通过另外两种鉴定方法发现外,所有方法均覆盖了所有的标记基因。
官网给的是8个群的marker,我们需要改成自己对应的9群的一个结果:
# Let us also define a list of marker genes for later reference
marker_genes = ['IL7R', 'CD79A', 'MS4A1', 'CD8A', 'CD8B', 'LYZ', 'CD14',
'LGALS3', 'S100A8', 'GNLY', 'NKG7', 'KLRB1',
'FCGR3A', 'MS4A7', 'FCER1A', 'CST3', 'PPBP']
重新加载之前Wilcoxon Rank-Sum test保存的数据
adata = sc.read(outdir + 'pbmc3k.h5ad')
# Show the 10 top ranked genes per cluster 0, 1, …, 7,8 in a dataframe.
pd.DataFrame(adata.uns['rank_genes_groups']['names']).head(5)
0 1 2 3 4 5 6 7 8
0 LDHB LYZ CD74 CCL5 NKG7 LST1 MALAT1 HLA-DPA1 PF4
1 RPS12 S100A9 CD79A NKG7 GNLY FCER1G RPL32 HLA-DPB1 SDPR
2 RPS25 S100A8 HLA-DRA CST7 GZMB FCGR3A RPL27A HLA-DRA GNG11
3 CD3D TYROBP CD79B B2M CTSW AIF1 RPS27 HLA-DRB1 PPBP
4 RPS3 FTL HLA-DPB1 GZMA PRF1 COTL1 RPL13 CD74 NRGN
得到一个分数和组表:
# Get a table with the scores and groups.
result = adata.uns['rank_genes_groups']
groups = result['names'].dtype.names
pd.DataFrame(
{group + '_' + key[:1]: result[key][group]
for group in groups for key in ['names', 'pvals']}).head(5)
与单个cluster进行比较:0 vs 1
# Compare to a single cluster:
sc.tl.rank_genes_groups(adata, 'leiden', groups=['0'], reference='1', method='wilcoxon')
sc.pl.rank_genes_groups(adata, groups=['0'], n_genes=20)
plt.savefig(outdir+'./08-marker1.jpg')
更详细的可视化:0 vs 1
sc.pl.rank_genes_groups_violin(adata, groups='0', n_genes=8)
plt.savefig(outdir+'./08-marker_violin.jpg')
加载之前的差异分析结果:看0 vs rest
adata = sc.read(outdir + 'pbmc3k.h5ad')
sc.pl.rank_genes_groups_violin(adata, groups='0', n_genes=8)
plt.savefig(outdir+'./09-marker_violin.jpg')
如果你想在不同cluster之间比较某个基因,请使用以下方法:
# compare a certain gene across groups
sc.pl.violin(adata, ['CST3', 'NKG7', 'PPBP'], groupby='leiden')
plt.savefig(outdir+'./09-marker_violin1.jpg',dpi=800,bbox_inches = 'tight')
'CST3', 'NKG7', 'PPBP'的结果:
定义细胞群:
# 首先看不同的marker在亚群中的表达情况:
# visualize the marker genes
sc.pl.dotplot(adata, marker_genes, groupby='leiden')
plt.savefig(outdir+'./09-marker_dotplot.jpg',dpi=800,bbox_inches = 'tight')
手动注释亚群:这里有个点,不支持多个cluster是同一种细胞类型的格式,
后来找到了解决办法,参考:https://mp.weixin.qq.com/s/cevuK4znyWLND4t5X-xrfA
# mark the cell types
new_cluster_names = [
'CD4 T',
'CD14 Monocytes',
'B',
'CD8 T',
'NK',
'FCGR3A Monocytes',
'CD4 T',
'Dendritic',
'Megakaryocytes']
adata.rename_categories('leiden', new_cluster_names)
#ValueError: Categorical categories must be unique
#不支持多个cluster是同一种细胞类型的格式,换一种方式添加细胞类型
# 换一种方式添加细胞类型
new_cluster_names_unique=list(set(new_cluster_names))
new_cluster_names_unique.sort(key=new_cluster_names.index)
celltype = [new_cluster_names[int(i)] for i in adata.obs.leiden.values.tolist()]
adata.obs.leiden = celltype
adata.obs.leiden = adata.obs.leiden.astype('category')
adata.obs.leiden.cat.set_categories(new_cluster_names_unique, inplace=True)
sc.pl.umap(adata, color='leiden', legend_loc='on data', title='', frameon=False)
plt.savefig(outdir+'./09-marker_annotation.jpg',dpi=1000,bbox_inches = 'tight')
注释之后的结果:
marker基因可视化:
## 注释之后的marker可视化
sc.pl.dotplot(adata, marker_genes, groupby='leiden')
plt.savefig(outdir+'./09-marker_annotation-dotplot.jpg',dpi=1000,bbox_inches = 'tight')
# 小提琴图
sc.pl.stacked_violin(adata, marker_genes, groupby='leiden', rotation=90);
plt.savefig(outdir+'./09-marker_annotation-violin.jpg',dpi=1000,bbox_inches = 'tight')
气泡图:
小提琴图:
最后数据保存:
## 最终结果保存
# `compression='gzip'` saves disk space, but slows down writing and subsequent reading
adata.write(outdir + 'pbmc3k.h5ad', compression='gzip')