ProjecTILs系列教程(二):ICB疗效预测

说在前面

免疫检查点抑制剂是肿瘤治疗的革命性药物,然而目前还不清楚为什么一些患者对检查点抑制剂疗法有反应,而另一些则没有。在Sade-Feldman等人2018年的一项Cell研究中,作者描述了免疫检查点封锁前后黑色素瘤患者免疫细胞的单细胞转录谱,目的是确定与检查点治疗成功或失败相关的因素。他们的研究表明,在肿瘤组织中发现的两种CD8 T细胞状态之间的平衡(本质上是记忆型和耗尽型)与检查点封锁后的肿瘤退化有关。特别是,肿瘤中TCF7+ CD8 T细胞的浸润程度预示着ICB应答和更好的生存时间。

那么本期推文,Immugent就通过实操使用ProjecTILs,来对各种肿瘤浸润T细胞亚型进行鉴定,从而对ICB治疗效果进行预测,这也同时可以检测利用小鼠参考图谱预测人的单细胞数据的效果。


代码实现

第一步还是要先准备输入数据。

library(ggplot2)
library(ProjecTILs)
library(gridExtra)

cached.object <- "SadeFeldman.seurat.rds"

if (!file.exists(cached.object)) {

    library(GEOquery)
    geo_acc <- "GSE120575"
    datadir <- "input/SadeFeldman"
    gse <- getGEO(geo_acc)

    series <- paste0(geo_acc, "_series_matrix.txt.gz")

    system(paste0("mkdir -p ", datadir))
    getGEOSuppFiles(geo_acc, baseDir = datadir)

    ## Load expression matrix and metadata
    exp.mat <- read.delim(sprintf("%s/%s/GSE120575_Sade_Feldman_melanoma_single_cells_TPM_GEO.txt.gz",
        datadir, geo_acc), header = F, sep = "\t")
    genes <- exp.mat[c(-1, -2), 1]
    cells <- as.vector(t(exp.mat[1, 2:16292]))
    samples <- as.factor(t(exp.mat[2, 2:16292]))

    exp.mat <- exp.mat[c(-1, -2), 2:16292]
    colnames(exp.mat) <- cells
    rownames(exp.mat) <- genes

    meta <- read.delim(sprintf("%s/%s/GSE120575_patient_ID_single_cells.txt.gz",
        datadir, geo_acc), header = T, sep = "\t", skip = 19, nrows = 16291)
    meta <- meta[, 1:7]

    treat <- factor(ifelse(grepl("Post", samples), "Post", "Pre"))
    response <- factor(meta$characteristics..response)
    therapy <- factor(meta$characteristics..therapy)

    ## Create Seurat object and add meta data
    query.object <- CreateSeuratObject(counts = exp.mat, project = "SadeFeldman",
        min.cells = 10)
    rm(exp.mat)
    [email protected]$Sample <- samples
    [email protected]$Time <- treat
    [email protected]$Response <- response
    [email protected]$Therapy <- therapy

    saveRDS(query.object, file = cached.object)
} else {
    query.object <- readRDS(cached.object)
}

query.object <- subset(query.object, subset = Time == "Pre")
table(query.object$Sample)

下面就是常规的ProjecTILs流程,需要注意的是和上一期使用的参考数据是不一样的。

ref <- load.reference.map()
query.projected <- make.projection(query.object, ref = ref)
plot.projection(ref, query.projected)
image.png
query.projected <- cellstate.predict(ref = ref, query = query.projected)
table(query.projected$functional.cluster)

query.list <- SplitObject(query.projected, split.by = "Response")
plot.states.radar(ref, query = query.list, min.cells = 50, genes4radar = c("Foxp3",
    "Cd4", "Cd8a", "Tcf7", "Ccr7", "Gzmb", "Pdcd1", "Havcr2", "Tox", "Entpd1", "Cxcr5",
    "Ifng", "Cxcl13", "Xcl1", "Itgae"))
image.png
data(Hs2Mm.convert.table)
which.genes <- c("TCF7", "GZMB", "CD8B", "PDCD1", "ITGAE")

Hs2Mm.convert.table[Hs2Mm.convert.table$Gene.HS %in% which.genes, ]

query.list <- SplitObject(query.projected, split.by = "Response")

pll <- list()

pll[[1]] <- plot.projection(ref, query.list[["Responder"]]) + ggtitle("Responder")
pll[[2]] <- plot.statepred.composition(ref, query.list[["Responder"]], metric = "Percent") +
    ggtitle("Responder") + ylim(0, 40)
pll[[3]] <- plot.projection(ref, query.list[["Non-responder"]]) + ggtitle("Non-responder")
pll[[4]] <- plot.statepred.composition(ref, query.list[["Non-responder"]], metric = "Percent") +
    ggtitle("Non-responder") + ylim(0, 40)

grid.arrange(grobs = pll, ncol = 2, nrow = 2, widths = c(1.5, 1))
image.png
which.types <- table(query.projected$functional.cluster) > 20

stateColors_func <- c("#edbe2a", "#A58AFF", "#53B400", "#F8766D", "#00B6EB", "#d1cfcc",
    "#FF0000", "#87f6a5", "#e812dd")
states_all <- levels(ref$functional.cluster)
names(stateColors_func) <- states_all
cols_use <- stateColors_func[names(which.types)][which.types]

# Responder vs non Responder
query.projected$functional.cluster <- factor(query.projected$functional.cluster,
    levels = states_all)
query.list <- SplitObject(query.projected, split.by = "Response")

norm.c <- table(query.list[["Non-responder"]]$functional.cluster)/sum(table(query.list[["Non-responder"]]$functional.cluster))
norm.q <- table(query.list[["Responder"]]$functional.cluster)/sum(table(query.list[["Responder"]]$functional.cluster))

foldchange <- norm.q[which.types]/norm.c[which.types]
foldchange <- sort(foldchange, decreasing = T)

tb.m <- melt(foldchange)
colnames(tb.m) <- c("Cell_state", "Fold_change")
pll <- list()
ggplot(tb.m, aes(x = Cell_state, y = Fold_change, fill = Cell_state)) + geom_bar(stat = "identity") +
    scale_fill_manual(values = cols_use) + geom_hline(yintercept = 1) + scale_y_continuous(trans = "log2") +
    theme(axis.text.x = element_blank(), legend.position = "left") + ggtitle("Responder vs. Non-responder")
image.png

利用投射函数的同源映射功能,在上面的结果中Immugent展示了如何利用参考小鼠TIL图谱,直接分析人类肿瘤浸润的scRNA-seq数据。基因表达谱证实了T细胞在主要的CD4+和CD8+类型,以及更具体的亚型(CD8_Tex, CD8_Tpex, Naive-like, Follicular helper, Th1, and T regulatory CD4+ cells)中进行准确的映射。


小结

对有应答和无应答的黑色素瘤患者在基线时TILs的转录谱和细胞状态的比较证实了Sade-Feldman等人的原始结论,即TCF7+ CD8 TILs的频率与检查点治疗的反应性相关。然而,与反应相关的TCF7+ CD8 TIL群体似乎对应的是一种幼稚状态,而不是先前在小鼠癌症和慢性感染模型中表征的(PD-1+ TOX+)前体耗尽状态。此外,这种幼稚样TIL群体不太可能具有肿瘤特异性,特别是在克隆扩增缺乏强有力证据的情况下,因为PD-1- CD8 TIL中肿瘤反应细胞的频率在黑色素瘤肿瘤中非常低。这似乎与其他研究不一致,这些研究表明PD-1+耗尽样CD8 T细胞的存在可以预测黑色素瘤和非小细胞肺癌对检查点封锁的反应。然而,在这个数据集中,来自有应答者和无应答者的大多数肿瘤都包含一个表达PD-1 (PDCD1)、CD39 (ENTPD1)、CXCL13、CD103 (ITGAE)的耗竭型CD8 T细胞,即使在无应答者中这种细胞的频率更高。

因此,肿瘤中PD-1+ CD8 T细胞的存在可能是治疗成功所必需的,但还不够。其他因素,包括CD8 TIL浸润总量、空间分布等,可能与幼稚型CD8 TIL的存在有关,并与改善的反应有关。例如,我们还能观察到,应答的肿瘤中Th1样和幼稚样CD4 T细胞的频率较高,而调节性CD4 T细胞的频率较低,这可能也有助于免疫治疗后抗肿瘤反应的改善。

总之,在稳定图谱的背景下,对人类T细胞数据的投射分析提供了一个稳定的框架来比较不同组和条件下的样本,并给出了与免疫治疗反应相关的T细胞状态的更完整的细胞构成。虽然通过上面的结果已经证明,稳健的同源信号可以通过将人类数据投影到参考小鼠图谱中来提取,但人-小鼠图谱绘制也将有利于构建稳定的人类图谱,其中个体间的可变性是一个主要障碍。

好啦,本期分享到这就结束啦,我们下期再会。


你可能感兴趣的:(ProjecTILs系列教程(二):ICB疗效预测)