常用的细胞通讯软件:
- CellphoneDB:是公开的人工校正的,储存受体、配体以及两种相互作用的数据库。此外,还考虑了结构组成,能够描述异构复合物。(配体-受体+多聚体)
- iTALK:通过平均表达量方式,筛选高表达的胚体和受体,根据结果作圈图。(配体-受体)
- CellChat:CellChat将细胞的基因表达数据作为输入,并结合配体受体及其辅助因子的相互作用来模拟细胞间通讯。(配体-受体+多聚体+辅因子)
- NicheNet // NicheNet多样本分析 // 目标基因的配体和靶基因活性预测:通过将相互作用细胞的表达数据与信号和基因调控网络的先验知识相结合来预测相互作用细胞之间的配体-靶标联系的方法。( 配体-受体+信号通路)
附:NicheNet使用的常见问题汇总其它细胞互作软件还包括
Celltalker
,SingleCellSignalR
,scTensor
和SoptSC
(这几个也是基于配体-受体相互作用)
1. CellPhoneDB介绍
CellPhoneDB是包含配体、受体及其相互作用的数据库,可以对细胞间的通讯分子进行全面、系统的分析,研究不同细胞类型之间的相互交流及通讯网络。
CellPhoneDB 是目前使用最广泛的细胞通讯分析软件,CellPhoneDB的配体-受体数据库集成于UniProt、Ensembl、PDB、IUPHAR等,共存储978种蛋白质:其中501种是分泌蛋白,而585种是膜蛋白,这些蛋白质参与1,396种相互作用。CellPhoneDB数据库还考虑了配体和受体的亚基结构,准确地表示了异聚体复合物,有466个是异聚体,对于准确研究多亚基复合物介导的细胞通讯很关键。CellPhoneDB有474种相互作用涉及分泌蛋白,而490种相互作用涉及膜蛋白,总共有250个涉及整合素的相互作用。
数据库链接:https://www.cellphonedb.org/ppi-resources。
目前支持的物种:人(也可通过人和鼠的同源基因比对应用于鼠)
2. CellPhoneDB原理
参考:https://www.cellphonedb.org/explore-sc-rna-seq
- CellphoneDB基于一种细胞类型的受体和另一种细胞类型的配体的表达,得出两种细胞群之间丰富的受体-配体相互作用。对于细胞群所表达的基因,计算表达该基因的细胞百分比和基因表达平均值。若该基因只在该群中10%及以下的细胞中表达(默认值为0.1),则被移除。
- 将所有细胞的簇标签随机排列1000次(可选值),并确定簇中受体平均表达水平和相互作用簇中配体平均表达水平的平均值。对于两种细胞类型之间每对比较中的每一个受体-配体对,这产生了一个零分布(null distribution,亦称伯努利分布、两点分布,指的是对于随机变量X有, 参数为p(0<p<1),如果它分别以概率p和1-p取1和0为值。EX= p,DX=p(1-p)。伯努利试验成功的次数服从伯努利分布,参数p是试验成功的概率)。
- 通过计算等于或高于实际平均值的平均值的比例,可以得到了一个P值,表示给定受体-配体复合物细胞类型特异性的可能性。换句话说,如果观察到的平均值在前5%,则相互作用的P值为0.05。
- 根据两种细胞类型中富集到的显著的受体-配体对的数量,对细胞类型之间高度特异性的相互作用进行排序,以便手动筛选出生物学相关的相互作用关系。
3. CellPhoneDB下载和使用
3.1 CellPhoneDB下载和主要功能介绍
安装:
CellPhoneDB软件仓库地址:https://github.com/Teichlab/cellphonedb
PIP安装:pip install cellphonedb(python版本需要python>3.5)
CellPhoneDB安装完成后,可以在终端测试是否成功。CellPhoneDB主要分成method、plot、query和database4个模块。
我们主要进行分析的是method(分析模块)和plot模块(画图模块)。
query是进行数据查询的模块,查询基因有哪些互作结果(get_interaction_gene结果为数据库涉及到的基因,find_interactions_by_element可以找到特定基因的受体配体作用数据对)。
database是输入数据库,一般默认可以不输入,直接使用即可。
- 四个重要的函数:
-
核心可选参数:
示例:
#(1)设置迭代和线程的数量
cellphonedb method statistical_analysis yourmetafile.txt yourcountsfile.txt --iterations=10 --threads=2
#(2)子项目文件夹
cellphonedb method analysis yourmetafile.txt yourcountsfile.txt --project-name=new_project
# (3)设置输出路径
mkdir custom_folder
cellphonedb method statistical_analysis yourmetafile.txt yourcountsfile.txt --output-path=custom_folder
#(4) 二次抽样
cellphonedb method analysis yourmetafile.txt yourcountsfile.txt --subsampling --subsampling-log false --subsampling-num-cells 3000
3.2 数据准备:注释好的pbmc3k(数据下载和准备见monocle)
pbmc3k <- readRDS("pbmc.rds")
由于细胞类型已经注释好,接下来准备cellphonedb的文件:表达谱文件cellphonedb_count.txt和细胞分组注释文件cellphonedb_meta.txt。
write.table(as.matrix(pbmc3k@assays$RNA@data), 'cellphonedb_count.txt', sep='\t', quote=F)
meta_data <- cbind(rownames(pbmc3k@meta.data), pbmc3k@meta.data[,'cell_type', drop=F])
meta_data <- as.matrix(meta_data)
meta_data[is.na(meta_data)] = "Unkown" # 细胞类型中不能有NA
write.table(meta_data, 'cellphonedb_meta.txt', sep='\t', quote=F, row.names=F)
3.3 终端运行cellphonedb:
安装好cellphonedb并加入环境变量后,使用终端运行如下代码:
cellphonedb method statistical_analysis cellphonedb_meta.txt cellphonedb_count.txt --counts-data=gene_name
#如果我们count的基因是基因名格式,需要添加参数--counts-data=gene_name,如果行名为ensemble名称的话,可以不添加这个参数,使用默认值即可。
终端运行完上述代码,在工作目录下会得到一个out文件夹,里面有四个txt:
deconvoluted.txt:基因在亚群中的平均表达量
mean.txt:每对受体-配体的平均表达量
pvalues.txt:每对受体-配体的p值
significant_means.txt:每对受体-配体显著性结果的平均表达量值
#cellphonedb 自己的绘图
cellphonedb plot dot_plot
cellphonedb plot heatmap_plot cellphonedb_meta.txt
tree out
#使用tree来查看cellphonedb生成的out文件夹下的文件。如果没有tree,可以使用brew install tree。
out
├── count_network.txt ## 绘制网络图文件。注意这个文件是在cellphonedb plot中生成的,所以上面两个plot不要漏掉了。
├── deconvoluted.txt
├── heatmap_count.pdf
├── heatmap_log_count.pdf
├── interaction_count.txt
├── means.txt ## 绘制点图文件
├── plot.pdf
├── pvalues.txt ## 绘制点图文件
└── significant_means.txt
得到3个图:
- plot.pdf
CellPhoneDB气泡图:每一列为两个细胞亚群(如:B|DC T),每一行为一对受体配体名称(如:CD2_CD58),颜色代表两个亚群这两个基因的平均表达量高低,越红表示表达越高,气泡大小代表P值的-log10值,气泡越大,说明其越具显著性。
- heatmap_count.pdf和heatmap_log_count.pdf
CellPhoneDB Heatmap:下面左图和右图其实都是对interaction_count.txt结果的展示,左图为亚群之间相互作用受体配体数目的热图,右图为将这个数目取了log10。
上述文件和图形的解读可以参考官网:https://www.cellphonedb.org/documentation
3.4 使用R对结果进行可视化(主要是网络图和点图)
pbmc='/practice/cellphonedb/out/' #outs下的文件放在这里了
library(psych)
library(qgraph)
library(igraph)
library(tidyverse)
netf<- "count_network.txt"
mynet <- read.delim(paste0(pbmc,"count_network.txt"), check.names = FALSE) #读取数据
table(mynet$count)
mynet %>% filter(count>0) -> mynet # 过滤count为0的数据(有零会报错)
head(mynet)
net<- graph_from_data_frame(mynet) #构建net对象
plot(net)
为了给这个网络图的边点mapping上不同的属性我们引入一串颜色
allcolour=c("#DC143C","#0000FF","#20B2AA","#FFA500","#9370DB",
"#98FB98","#F08080","#1E90FF","#7CFC00","#FFFF00",
"#808000","#FF00FF","#FA8072","#7B68EE","#9400D3",
"#800080","#A0522D","#D2B48C","#D2691E","#87CEEB",
"#40E0D0","#5F9EA0","#FF1493",
"#FFE4B5","#8A2BE2","#228B22","#E9967A","#4682B4",
"#32CD32","#F0E68C","#FFFFE0","#EE82EE","#FF6347",
"#6A5ACD","#9932CC","#8B008B","#8B4513","#DEB887")
karate_groups <- cluster_optimal(net) #统计每个端点的和
coords <- layout_in_circle(net, order =
order(membership(karate_groups))) # 设置网络布局
E(net)$width <- E(net)$count/10 #根据count值设置边的宽
plot(net, edge.arrow.size=.1,
edge.curved=0,
vertex.color=allcolour,
vertex.frame.color="#555555",
vertex.label.color="black",
layout = coords,
vertex.label.cex=.7)
但是边的颜色和点的颜色还是对应不上,我们来修改一番边的属性。
net2 <- net # 复制一份备用
for (i in 1: length(unique(mynet$SOURCE)) ){ #配置发出端的颜色
E(net)[map(unique(mynet$SOURCE),function(x) {
get.edge.ids(net,vp = c(unique(mynet$SOURCE)[i],x))
})%>% unlist()]$color <- allcolour[i]
} # 这波操作谁有更好的解决方案?
plot(net, edge.arrow.size=.1,
edge.curved=0,
vertex.color=allcolour,
vertex.frame.color="#555555",
vertex.label.color="black",
layout = coords,
vertex.label.cex=.7)
我们观察到由于箭头是双向的,所以两个细胞类型之间的线条会被后来画上去的覆盖掉,这里我们把线条做成曲线:
plot(net, edge.arrow.size=.1, #箭头大小设置为0.1
edge.curved=0.2, # 只是调了曲率这个参数
vertex.color=allcolour,
vertex.frame.color="#555555", #圆圈颜色
vertex.label.color="black", #标签颜色
layout = coords, #网络布局位点
vertex.label.cex=.7#标签大小)
接下来,我们来绘制第二组类型贝壳一样的网络图。
dev.off()
length(unique(mynet$SOURCE)) # 查看需要绘制多少张图,以方便布局
par(mfrow=c(2,5), mar=c(.3,.3,.3,.3))
for (i in 1: length(unique(mynet$SOURCE)) ){
net1<-net2
E(net1)[map(unique(mynet$SOURCE),function(x) {
get.edge.ids(net,vp = c(unique(mynet$SOURCE)[i],x))
})%>% unlist()]$color <- allcolour[i]
plot(net1, edge.arrow.size=.1,
edge.curved=0.4,
vertex.color=allcolour,
vertex.frame.color="#555555",
vertex.label.color="black",
layout = coords,
vertex.label.cex=1)
}
但是,细胞间通讯的频数(count)并没有绘制在图上,略显不足,这就补上吧。
dev.off()
length(unique(mynet$SOURCE))
par(mfrow=c(2,5), mar=c(.3,.3,.3,.3))
for (i in 1: length(unique(mynet$SOURCE)) ){
net1<-net2
E(net1)$count <- ""
E(net1)[map(unique(mynet$SOURCE),function(x) {
get.edge.ids(net,vp = c(unique(mynet$SOURCE)[i],x))
})%>% unlist()]$count <- E(net2)[map(unique(mynet$SOURCE),function(x) {
get.edge.ids(net,vp = c(unique(mynet$SOURCE)[i],x))
})%>% unlist()]$count # 故技重施
E(net1)[map(unique(mynet$SOURCE),function(x) {
get.edge.ids(net,vp = c(unique(mynet$SOURCE)[i],x))
})%>% unlist()]$color <- allcolour[i]
plot(net1, edge.arrow.size=.1,
edge.curved=0.4,
edge.label = E(net1)$count, # 绘制边的权重
vertex.color=allcolour,
vertex.frame.color="#555555",
vertex.label.color="black",
layout = coords,
vertex.label.cex=1
)
}
找两条边验证一下对应关系。
mynet %>% filter(SOURCE == 'B',TARGET == 'DC')
SOURCE TARGET count
1 B DC 13
mynet %>% filter(SOURCE == 'Memory CD4 T',TARGET == 'B')
SOURCE TARGET count
1 Memory CD4 T B 15
用ggplot2 绘制点图,关键是把两张表合并到一起。
mypvals <- read.delim(paste0(pbmc,"pvalues.txt"), check.names = FALSE)
mymeans <- read.delim(paste0(pbmc,"means.txt"), check.names = FALSE)
# 这些基因list很有意思啊,建议保存
chemokines <- grep("^CXC|CCL|CCR|CX3|XCL|XCR", mymeans$interacting_pair,value = T)
chemokines <- grep("^CXC|CCL|CCR|CX3|XCL|XCR", mymeans$interacting_pair,value = T)
th1 <- grep("IL2|IL12|IL18|IL27|IFNG|IL10|TNF$|TNF |LTA|LTB|STAT1|CCR5|CXCR3|IL12RB1|IFNGR1|TBX21|STAT4",
mymeans$interacting_pair,value = T)
th2 <- grep("IL4|IL5|IL25|IL10|IL13|AREG|STAT6|GATA3|IL4R",
mymeans$interacting_pair,value = T)
th17 <- grep("IL21|IL22|IL24|IL26|IL17A|IL17A|IL17F|IL17RA|IL10|RORC|RORA|STAT3|CCR4|CCR6|IL23RA|TGFB",
mymeans$interacting_pair,value = T)
treg <- grep("IL35|IL10|FOXP3|IL2RA|TGFB", mymeans$interacting_pair,value = T)
costimulatory <- grep("CD86|CD80|CD48|LILRB2|LILRB4|TNF|CD2|ICAM|SLAM|LT[AB]|NECTIN2|CD40|CD70|CD27|CD28|CD58|TSLP|PVR|CD44|CD55|CD[1-9]",
mymeans$interacting_pair,value = T)
coinhibitory <- grep("SIRP|CD47|ICOS|TIGIT|CTLA4|PDCD1|CD274|LAG3|HAVCR|VSIR",
mymeans$interacting_pair,value = T)
niche <- grep("CSF", mymeans$interacting_pair,value = T)
mymeans %>% dplyr::filter(interacting_pair %in% costimulatory)%>%
dplyr::select("interacting_pair",starts_with("NK"),ends_with("NK")) %>%
reshape2::melt() -> meansdf
colnames(meansdf)<- c("interacting_pair","CC","means")
mypvals %>% dplyr::filter(interacting_pair %in% costimulatory)%>%
dplyr::select("interacting_pair",starts_with("NK"),ends_with("NK"))%>%
reshape2::melt()-> pvalsdf
colnames(pvalsdf)<- c("interacting_pair","CC","pvals")
pvalsdf$joinlab<- paste0(pvalsdf$interacting_pair,"_",pvalsdf$CC)
meansdf$joinlab<- paste0(meansdf$interacting_pair,"_",meansdf$CC)
pldf <- merge(pvalsdf,meansdf,by = "joinlab")
summary((filter(pldf,means >1))$means)
pldf%>% filter(means >1) %>%
ggplot(aes(CC.x,interacting_pair.x) )+
geom_point(aes(color=means,size=-log10(pvals+0.0001)) ) +
scale_size_continuous(range = c(1,3))+
scale_color_gradient2(high="red",mid = "yellow",low ="darkblue",midpoint = 25 )+ theme_bw()+
theme(axis.text.x = element_text(angle = -45,hjust = -0.1,vjust = 0.8))