好吧, 我们来继续一些转录组 + VDJ的联合分析,今天分享的软件是scirpy,很多人应该用过,文章在Scirpy: A Scanpy extension for analyzing single-cell T-cell receptor sequencing data,发表于Bioinformatics,影响因子7分,其实这个软件也是做了一些基础的联合分析,并不能分析的很深入,但还是很值得借鉴,方法还是很不错的。
Analysis of 3k T cells from cancer,我们以官方为例,看看主要的分析内容。 原始数据集由来自 14 名未经治疗的患者的 14 万多个 T 细胞组成,这些患者来自四种不同类型的癌症。但若干,scripy可以直接读取10X的分析结果数据。其他的数据格式之前都分享过,大家可以回看。
# Load the TCR data
adata_tcr = ir.io.read_10x_vdj(
"example_data/liao-2019-covid19/GSM4385993_C144_filtered_contig_annotations.csv.gz"
)
# Load the associated transcriptomics data
adata = sc.read_10x_h5(
"example_data/liao-2019-covid19/GSM4339772_C144_filtered_feature_bc_matrix.h5"
)
加载
import numpy as np
import pandas as pd
import scanpy as sc
import scirpy as ir
from matplotlib import pyplot as plt, cm as mpl_cm
from cycler import cycler
sc.set_figure_params(figsize=(4, 4))
sc.settings.verbosity = 2 # verbosity: errors (0), warnings (1), info (2), hints (3)
sc.logging.print_header()
adata = ir.datasets.wu2020_3k() ##数据
Preprocess Transcriptomics data,基础的scanpy分析流程
sc.pp.filter_genes(adata, min_cells=10)
sc.pp.filter_cells(adata, min_genes=100)
sc.pp.normalize_per_cell(adata, counts_per_cell_after=1000)
sc.pp.log1p(adata)
sc.pp.highly_variable_genes(adata, flavor="cell_ranger", n_top_genes=5000)
sc.tl.pca(adata)
sc.pp.neighbors(adata)
接下来直接使用作者提供的UMAP坐标
adata.obsm["X_umap"] = adata.obsm["X_umap_orig"]
mapping = {
"nan": "other",
"3.1-MT": "other",
"4.1-Trm": "CD4_Trm",
"4.2-RPL32": "CD4_RPL32",
"4.3-TCF7": "CD4_TCF7",
"4.4-FOS": "CD4_FOSS",
"4.5-IL6ST": "CD4_IL6ST",
"4.6a-Treg": "CD4_Treg",
"4.6b-Treg": "CD4_Treg",
"8.1-Teff": "CD8_Teff",
"8.2-Tem": "CD8_Tem",
"8.3a-Trm": "CD8_Trm",
"8.3b-Trm": "CD8_Trm",
"8.3c-Trm": "CD8_Trm",
"8.4-Chrom": "other",
"8.5-Mitosis": "other",
"8.6-KLRB1": "other",
}
adata.obs["cluster"] = [mapping[x] for x in adata.obs["cluster_orig"]]
初步的绘图
sc.pl.umap(
adata,
color=["sample", "patient", "cluster", "CD8A", "CD4", "FOXP3"],
ncols=2,
wspace=0.7,
)
TCR Quality Control(TCR部分的分析),虽然大多数 T 细胞受体恰好具有一对 α 和 β 链,但多达三分之一的 T 细胞可以具有双重 TCR,即来自不同等位基因的两对受体 .
使用 script.to.chain_qc() 函数,我们可以向 adata.obs 添加有关免疫细胞受体组成的信息。 我们可以使用 script.pl.group 丰度()对其进行可视化。
链配对
Orphan chain是指具有单个α 或β 受体链的细胞。
额外链是指具有完整 α/β 受体对和附加链的细胞。
多链是指检测到两个以上受体对的细胞。 这些细胞很可能是双联体。
受体类型和受体亚型
受体类型是指粗略分类为 BCR 和 TCR。 具有 BCR 和 TCR 链的细胞被标记为不明确的。
受体亚型是指更具体的分类为 α/β、ɣ/δ、IG-λ 和 IG-κ 链构型。
有关更多详细信息,请参阅 scirpy.tl.chain_qc()。
ir.tl.chain_qc(adata) ##As expected, the dataset contains only α/β T-cell receptors
ax = ir.pl.group_abundance(adata, groupby="receptor_subtype", target_col="source")
ax = ir.pl.group_abundance(adata, groupby="chain_pairing", target_col="source")
事实上,在这个数据集中,约 6% 的细胞拥有超过一对的productive T-cell receptors,接下来,我们在 UMAP 图上可视化多链细胞并将它们从下游分析中排除:
sc.pl.umap(adata, color="chain_pairing", groups="multichain")
adata = adata[adata.obs["chain_pairing"] != "multichain", :].copy()
##Similarly, we can use the chain_pairing information to exclude all cells that don’t have at least one full pair of receptor sequences:
adata = adata[~adata.obs["chain_pairing"].isin(["orphan VDJ", "orphan VJ"]), :].copy()
##Finally, we re-create the chain-pairing plot to ensure that the filtering worked as expected:
ax = ir.pl.group_abundance(adata, groupby="chain_pairing", target_col="source")
接下来重点,Define clonotypes and clonotype clusters
Scirpy 实现了一种基于网络的克隆型定义方法。 创建和可视化克隆型网络的步骤类似于使用 scanpy 从转录组学数据构建邻域图。
计算CDR3邻域图并定义克隆型
scirpy.pp.ir_dist() 根据序列同一性或相似性计算 CDR3 核苷酸 (nt) 或氨基酸 (aa) 序列之间的距离。它创建两个距离矩阵:一个用于所有独特的 VJ 序列,另一个用于所有独特的 VDJ 序列。距离矩阵被添加到 adata.uns。
函数 scirpy.tl.define_clonotypes() 根据细胞的 VJ 和 VDJ CDR3 序列的距离以及函数参数 dual_ir 和 receptor_arms 的值来匹配细胞。最后,它检测图中的连接模块并将它们注释为克隆型。这将向 adata.obs 添加一个 clone_id 和 clone_id_size 列。
dual_ir 参数定义了 scipy 如何处理具有一对以上受体的细胞。默认值是 any ,这意味着具有任何初级或次级受体链匹配的细胞将被认为是相同的克隆型。
在这里,根据 nt 序列身份定义克隆型。在后面的步骤中,我们将根据氨基酸相似性定义克隆型簇。
# using default parameters, `ir_dist` will compute nucleotide sequence identity
ir.pp.ir_dist(adata)
ir.tl.define_clonotypes(adata, receptor_arms="all", dual_ir="primary_only")
为了可视化网络,首先调用 scirpy.tl.clonotype_network() 来计算布局。 然后可以使用 scirpy.pl.clonotype_network() 对其进行可视化。 建议将 min_cells 参数设置为 >=2,以防止单例克隆型混乱网络。
ir.tl.clonotype_network(adata, min_cells=2)
结果图是一个网络,其中每个点代表具有相同受体配置的细胞。 当将克隆型定义为具有相同 CDR3 序列的细胞时,每个点也是一个克隆型。 对于每个克隆型,数字克隆型 ID 显示在图中。 每个点的大小是指具有相同受体配置的细胞数量。 分类变量可以可视化为饼图。
ir.pl.clonotype_network(
adata, color="source", base_size=20, label_fontsize=9, panel_size=(7, 7)
)
重新计算 CDR3 邻域图并定义克隆型cluster
现在可以根据氨基酸序列相似性重新计算克隆型网络并定义克隆型簇。
为此,需要更改 set metric="alignment" 并指定一个截止参数。 距离基于 BLOSUM62 矩阵。 例如,距离为 10 相当于 2 个 R 变异为 N。 这个方法最初是由 Dash 等人提出的 TCRdist。
其CDR3序列之间的距离小于cutoff的所有单元都将在网络中连接。
ir.pp.ir_dist(
adata,
metric="alignment",
sequence="aa",
cutoff=15,
)
ir.tl.define_clonotype_clusters(
adata, sequence="aa", metric="alignment", receptor_arms="all", dual_ir="any"
)
ir.tl.clonotype_network(adata, min_cells=3, sequence="aa", metric="alignment")
与之前的图相比,观察到了几个相连的点。 每个完全连接的子网络代表一个“克隆型簇”,每个点仍然代表具有相同受体配置的细胞。
点由患者着色。 观察到,例如,克隆型 101 和 68(左上和左下)是私有的,即它们仅包含来自单个患者的细胞。 另一方面,克隆型 159(左中)是公开的,即它在患者 Lung1 和 Lung3 之间共享。
ir.pl.clonotype_network(
adata, color="patient", label_fontsize=9, panel_size=(7, 7), base_size=20
)
现在可以通过子集 AnnData 从特定克隆型簇中提取信息(例如 CDR3 序列)。 在提取克隆型簇 159 的 CDR3 序列时,我们检索了具有不同细胞数量的五种不同受体配置,对应于图中的五个点。
adata.obs.loc[adata.obs["cc_aa_alignment"] == "159", :].groupby(
[
"IR_VJ_1_junction_aa",
"IR_VJ_2_junction_aa",
"IR_VDJ_1_junction_aa",
"IR_VDJ_2_junction_aa",
"receptor_subtype",
],
observed=True,
).size().reset_index(name="n_cells")
在克隆型定义中包括 V 基因
使用define_clonotypes() 中的参数use_v_gene,可以强制克隆型(或克隆型簇)具有相同的V 基因,因此具有相同的CDR1 和2 区域。 寻找具有不同 V 基因的克隆型簇:
ir.tl.define_clonotype_clusters(
adata,
sequence="aa",
metric="alignment",
receptor_arms="all",
dual_ir="any",
same_v_gene=True,
key_added="cc_aa_alignment_same_v",
)
# find clonotypes with more than one `clonotype_same_v`
ct_different_v = adata.obs.groupby("cc_aa_alignment").apply(
lambda x: x["cc_aa_alignment_same_v"].nunique() > 1
)
ct_different_v = ct_different_v[ct_different_v].index.values.tolist()
ct_different_v
在这里,看到当设置 same_v_gene 标志时,克隆型簇 280 和 765 分别分为 (280, 788) 和 (765, 1071)。
adata.obs.loc[
adata.obs["cc_aa_alignment"].isin(ct_different_v),
[
"cc_aa_alignment",
"cc_aa_alignment_same_v",
"IR_VJ_1_v_call",
"IR_VDJ_1_v_call",
],
].sort_values("cc_aa_alignment").drop_duplicates().reset_index(drop=True)
Clonotype analysis
Clonal expansion
按细胞类型可视化扩展克隆型(即由多个细胞组成的克隆型)的数量。 第一个选项是将带有 scirpy.tl.clonal_expansion() 的列添加到 adata.obs 并将其覆盖在 UMAP 图上。
ir.tl.clonal_expansion(adata)###clonal_expansion refers to expansion categories, i.e singleton clonotypes, clonotypes with 2 cells and more than 2 cells. The clonotype_size refers to the absolute number of cells in a clonotype.
sc.pl.umap(adata, color=["clonal_expansion", "clone_id_size"])
第二个选项是使用 scirpy.pl.clonal_expansion() 绘图函数在堆积条形图中显示属于每个类别的扩展克隆型的细胞数量。
ir.pl.clonal_expansion(adata, groupby="cluster", clip_at=4, normalize=False)
相同的图,归一化为集群大小。 克隆扩增是对某个反应性 T 细胞克隆进行阳性选择的标志。 因此,CD8+ 效应 T 细胞具有最大比例的扩增克隆型是有道理的。
ir.pl.clonal_expansion(adata, "cluster")
预计 CD8+ 效应 T 细胞具有最大比例的扩增克隆型。
与这一观察结果一致,它们具有最低的 scirpy.pl.alpha_diversity() 克隆型。
ax = ir.pl.alpha_diversity(
adata, metric="normalized_shannon_entropy", groupby="cluster"
)
Clonotype abundance
函数 scirpy.pl.group_abundance() 允许为来自 obs 的任意类别创建条形图。 在这里,我们用它来显示十个最大的克隆型在细胞类型簇中的分布。
ir.pl.group_abundance(adata, groupby="clone_id", target_col="cluster", max_cols=10)
将计数归一化为每个样本的细胞数以减轻由于不同样本大小引起的偏差可能是有益的:
ir.pl.group_abundance(
adata, groupby="clone_id", target_col="cluster", max_cols=10, normalize="sample"
)
按患者对条形着色可提供有关公共和私人克隆型的信息:一些克隆型是私人的,即特定于某个组织,而其他的则是公共的,即它们在不同组织之间共享。
ax = ir.pl.group_abundance(
adata, groupby="clone_id", target_col="source", max_cols=15, figsize=(5, 3)
)
However, clonotypes that are shared between patients are rare:
ax = ir.pl.group_abundance(
adata, groupby="clone_id", target_col="patient", max_cols=15, figsize=(5, 3)
)
Gene usage
scirpy.tl.group_abundance() 也可以给我们一些关于 VDJ 使用的信息。 我们可以选择任何 {TRA,TRB}{1,2}{v,d,j,c}_gene 列来制作堆积条形图。 我们使用 max_col 将图限制为 10 个最丰富的 V 基因。
ax = ir.pl.group_abundance(
adata, groupby="IR_VJ_1_v_call", target_col="cluster", normalize=True, max_cols=10
)
ax = ir.pl.group_abundance(
adata[
adata.obs["IR_VDJ_1_v_call"].isin(
["TRBV20-1", "TRBV7-2", "TRBV28", "TRBV5-1", "TRBV7-9"]
),
:,
],
groupby="cluster",
target_col="IR_VDJ_1_v_call",
normalize=True,
)
The exact combinations of VDJ genes can be visualized as a Sankey-plot using scirpy.pl.vdj_usage()
.
ax = ir.pl.vdj_usage(adata, full_combination=False, max_segments=None, max_ribbons=30)
We can also use this plot to investigate the exact VDJ composition of one (or several) clonotypes:
ir.pl.vdj_usage(
adata[adata.obs["clone_id"].isin(["68", "101", "127", "161"]), :],
max_ribbons=None,
max_segments=100,
)
Spectratype plots
ir.pl.spectratype(adata, color="cluster", viztype="bar", fig_kws={"dpi": 120})
ir.pl.spectratype(
adata,
color="cluster",
viztype="curve",
curve_layout="shifted",
fig_kws={"dpi": 120},
kde_kws={"kde_norm": False},
)
ir.pl.spectratype(
adata[
adata.obs["IR_VDJ_1_v_call"].isin(
["TRBV20-1", "TRBV7-2", "TRBV28", "TRBV5-1", "TRBV7-9"]
),
:,
],
cdr3_col="IR_VDJ_1_junction_aa",
color="IR_VDJ_1_v_call",
normalize="sample",
fig_kws={"dpi": 120},
)
Comparing repertoires
Repertoire simlarity and overlaps
样本或样本组的适应性免疫受体库的重叠能够确定重要的克隆型组,并提供样本之间相似性的度量。 运行 Scirpy 的 repertoire_overlap() 工具创建一个矩阵,其中包含每个样本中克隆型的丰度。 此外,它还计算样本的(Jaccard)距离矩阵以及层次聚类的链接。
df, dst, lk = ir.tl.repertoire_overlap(adata, "sample", inplace=False)
df.head()
The distance matrix can be shown as a heatmap, while samples are reordered based on hierarchical clustering.
ir.pl.repertoire_overlap(adata, "sample", heatmap_cats=["patient", "source"])
A specific pair of samples can be compared on a scatterplot, where dot size corresponds to the number of clonotypes at a given coordinate.
ir.pl.repertoire_overlap(
adata, "sample", pair_to_plot=["LN2", "LT2"], fig_kws={"dpi": 120}
)
Integrating gene expression data(联合分析)
Clonotype modularity
使用克隆型模块性,我们可以识别由在转录上比随机预期更相似的细胞组成的克隆型。
克隆型模块性分数表示与随机背景模型相比,细胞-细胞邻域图中边数的 log2 倍变化。 具有高模块化评分的克隆型(或克隆型簇)由具有相似分子表型的细胞组成。
ir.tl.clonotype_modularity(adata, target_col="cc_aa_alignment")
sc.pl.umap(adata, color="clonotype_modularity")
ir.pl.clonotype_network(
adata,
color="clonotype_modularity",
label_fontsize=9,
panel_size=(6, 6),
base_size=20,
)
ir.pl.clonotype_modularity(adata, base_size=20)
Let’s further inspect the two top scoring candidates. We can extract that information from adata.obs["clonotype_modularity"].
clonotypes_top_modularity = list(
adata.obs.set_index("cc_aa_alignment")["clonotype_modularity"]
.sort_values(ascending=False)
.index.unique().values[:2]
)
sc.pl.umap(
adata,
color="cc_aa_alignment",
groups=clonotypes_top_modularity,
palette=cycler(color=mpl_cm.Dark2_r.colors),
)
观察到它们(大部分)仅限于单个集群。 通过利用 scanpy 的差异表达模块,我们可以将两种克隆型中细胞的基因表达与其余细胞进行比较。
sc.tl.rank_genes_groups(
adata, "clone_id", groups=clonotypes_top_modularity, reference="rest", method="wilcoxon"
)
fig, axs = plt.subplots(1, 2, figsize=(8, 4))
for ct, ax in zip(clonotypes_top_modularity, axs):
sc.pl.rank_genes_groups_violin(adata, groups=[ct], n_genes=15, ax=ax, show=False, strip=False)
Clonotype imbalance among cell clusters
例如,使用从基因表达簇推断的细胞类型注释,可以比较属于 CD8+ 效应 T 细胞和 CD8+ 组织驻留记忆 T 细胞的克隆型。
freq, stat = ir.tl.clonotype_imbalance(
adata,
replicate_col="sample",
groupby="cluster",
case_label="CD8_Teff",
control_label="CD8_Trm",
inplace=False,
)
top_differential_clonotypes = stat["clone_id"].tolist()[:3]
Showing top clonotypes on a UMAP clearly shows that clonotype 101 is featured by CD8+ tissue-resident memory T cells, while clonotype 68 by CD8+ effector and effector memory cells.
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4), gridspec_kw={"wspace": 0.6})
sc.pl.umap(adata, color="cluster", ax=ax1, show=False)
sc.pl.umap(
adata,
color="clone_id",
groups=top_differential_clonotypes,
ax=ax2,
# increase size of highlighted dots
size=[
80 if c in top_differential_clonotypes else 30 for c in adata.obs["clone_id"]
],
palette=cycler(color=mpl_cm.Dark2_r.colors),
)
Repertoire overlap of cell types
就像比较样本之间的曲目重叠一样,Scirpy 还提供基因表达簇或细胞亚群之间的比较。 例如,显示了上面比较的两种细胞类型的曲目重叠。
ir.tl.repertoire_overlap(adata, "cluster")
ir.pl.repertoire_overlap(
adata, "cluster", pair_to_plot=["CD8_Teff", "CD8_Trm"], fig_kws={"dpi": 120}
)
Marker genes in top clonotypes
sc.tl.rank_genes_groups(
adata, "clone_id", groups=["101"], reference="68", method="wilcoxon"
)
sc.pl.rank_genes_groups_violin(adata, groups="101", n_genes=15)
其实还是集中在分析克隆的基因选择和多样性,不如CoNGA。
生活很好,有你更好