R语言Circos图可视化

9 篇文章 5 订阅

准备数据

需要准备两个数据:一个是基因表达谱,另一个是基因的注释(可以为KO注释,也可以是别的什么注释)

基因表达谱

sample1sample2sample3...
gene11.02.02.0...
gene23.03.04.0...
gene35.05.05.0...
gene46.07.09.0...
...............

通路信息

geneKOpathway
gene1KO1pathway1
gene2KO2pathway1
gene3KO2pathway2
.........

模拟数据

library(tidyverse)
library(magrittr)
library(circlize)
#模拟数据
## Data1
fpkm <- rbind(cbind(matrix(rnorm(500*3, mean = 1), nr = 500), 
                   matrix(rnorm(500*3, mean = 2), nr = 500),
                   matrix(rnorm(500*3, mean = 3), nr = 500)))
fpkm <- fpkm[sample(500, 500), ] # randomly permute rows
rownames(fpkm) <- paste0("gene", seq(500))
colnames(fpkm) <- c("A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3")
fpkm %<>% as.data.frame() %>% mutate(gene = row.names(.))
# Data2
pathways <- rep(paste0("pathway", seq(6)),  sample(12:20, size = 6)) %>% sample(70)
KOs <- rep(paste0("KO", seq(20)),  sample(5:20, size = 20, replace = TRUE)) %>% sample(70)
KOannotation <- data.frame(KO=KOs, pathway=pathways)
KOannotation <- KOannotation[sample(70, 200, TRUE),]
KOannotation$gene <- sample(paste0("gene", seq(500)),200)
             
# 假设你有富集到的想要可视化的通路
maps <- c("pathway1", "pathway2", "pathway3")
samples <- c("A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3")

思考画图数据类型

首先简单介绍一下circos对象。正如普通的图有x轴和Y轴,你可以理解为一个circos图有很多个轴(具体数量由你数据确定)。那么每个轴上自然就有对应位置(如下图中的1、2、3、4)。

那么自然而然的很容易想象,如果要画link,需要一个类似这样的数据

from_axisfrom_positionto_axisto_position
A1B4
A2C5
A3D6

进一步的,如果要控制link的宽度,那应该需要规定每个link的起始和结束的位置

from_axisfrom_position_startfrom_position_endto_axisto_position_startto_position_end
A0.51.5B3.54.5
A1.52.5C4.55.5
A2.53.5D5.56.5

考虑完link之后,考虑热图。首先确认是用circos.heatmap()画的热图(如上图)。这个数据比较简单,因此考虑先画出热图,再考虑怎么画中间的和弦图。

热图

数据清洗

# 准备热图颜色
col_fun1 = colorRamp2(c(-2, 0, 2), c("#247ab5", "white", "#fda1a0"))
# 需要画图的基因
plot_gene <- KOannotation %>% 
  filter(pathway %in% maps) %>% 
  pull(gene) %>%
  {filter(fpkm, row.names(fpkm) %in% .)}
# 需要画图的KO
plot_KO <- plot_gene %>% 
  left_join(KOannotation) %>%
  filter(pathway %in% maps) %>% # There are some unenriched map
  group_by(KO) %>%
  summarise(across(samples,sum))
# 需要画图的Pathway
plot_map <- plot_gene %>% 
    left_join(KOannotation) %>%
    filter(pathway %in% maps) %>% # There are some unenriched map
    group_by(pathway) %>%
    summarise(across(samples,sum))

plot_data1 <- bind_rows(plot_gene %>% rename(id=gene), 
                        plot_KO %>% rename(id=KO), 
                        plot_map %>% rename(id=pathway)) %>%
    `row.names<-`(.$id) %>%
    select(-id)
plot_data1 <- t(scale(t(plot_data1))) %>% as.data.frame()
# 在热图上划分为gene、KO、pathway
lev_split = row.names(plot_data1) %>% str_match("[a-zA-Z]+") %>% factor()

画图

circos.clear()
circos.par(gap.degree=10, track.height=0.1)
# 分多次画表达谱数据,更有层次
circos.heatmap(plot_data1[samples[1:3]], split = lev_split , col = col_fun1, rownames.side = "outside", cluster = TRUE)
circos.heatmap(plot_data1[samples[7:9]], split = lev_split , col = col_fun1)
circos.heatmap(plot_data1[samples[4:6]], split = lev_split , col = col_fun1)

根据lev_split,这个热图划分为gene,KO,和pathway三个轴。这里需要指出的是,每个gene、KO、pathway在每个轴上占的长度都是1,例如gene67在gene轴上的位置为0-1,pathway2在pathway轴上的位置为0-1.因此我们要画link的话,根据之前的讨论,如果有三个KO需要连接到pathway2,应该需要一个类似下面的数据:

from_axisfrom_position_startfrom_position_endto_axisto_position_startto_position_end
KO0.51.5pathway00.333
KO1.52.5pathway0.3330667
KO2.53.5pathway0.6671

注意上表,可能存在多个KO连接到一个pathway的情况,因此我们要对start和end位置进行合理的拆分,以避免重叠。这样做的另一个好处是可以让link线有粗有细,看上去美观许多。所以

另一方面我们要注意,由于circos热图上的基因的是根据聚类结果排布的,所以和数据框里的数据顺序已经不一样了,因此我们首先需要获取画图后的每个gene、KO、pathway在其对应轴上的坐标,这时就需要用到circlizeget.cell.meta.data从图上获得相应信息

和弦图

数据清洗

根据上面的讨论结果,我们的数据清洗要实现两个目的:

  1. 获取热图生成后的gene、KO、pathway上每个轴的信息,可以整理成如下格式:

    idsectorposition
    gene1gene23
    KO1KO45
    pathway1pathway56

    表A

  2. 将一对多的gene-KO关系、KO-pathway关系进行计算,以获得相对的位置

    from_axisfrom_position_startfrom_position_endto_axisto_position_startto_position_end
    gene0.51.5KO55.33
    gene1.52.5KO5.335.66
    gene2.53.5KO5.666

    表B

    注意观察上表,由于3个基因连接到了相同的KO,所以我将他们分别连接到了KO不同的位置

    进一步,想要获得这个表,可以把过程拆分为以下几步:

    2.1生成一个连接对象表

    fromto
    gene1KO2
    gene2KO1
    KO1pathway1

    2.2 根据连接对象在表中出现的次数,再计算一个位移

    fromtofrom_startfrom_endto_startto_end
    gene1KO20100.333
    gene2KO1010.3330.667
    KO1pathway100.50.6671
    KO1pathway20.5100.0333

    表C

    2.3 结合表A和表C,计算得到表B

    明确思路以后,下面是代码

# 1 获得gene、KO、pathway在每个轴上的位置
plot_data3 = data.frame()
for(lev in levels(lev_split)){
  a <- rownames(plot_data1)[lev_split==lev][get.cell.meta.data("row_order", sector.index = lev)]
  a <- seq(length(a)) %>% `names<-`(a) %>% enframe("id", "position")
  a$sector = lev
  plot_data3 <- rbind(plot_data3, a)
} 
plot_data3$position <- plot_data3$position - 1 # 因为每个元素的范围是0~1,所以统一减一方便后面相加
#2.1 获取点对点连接表
plot_data2 <- KOannotation %>%
    filter(gene %in% row.names(plot_data1), KO %in% row.names(plot_data1)) %>%
    select(gene, KO) %>%
    rename(from=gene, to=KO)
tmp_obj1 <- KOannotation %>%
    filter(KO %in% row.names(plot_data1), pathway %in% row.names(plot_data1)) %>%
    select(KO, pathway) %>%
    rename(from=KO, to=pathway)
plot_data2 %<>% bind_rows(tmp_obj1) %>% distinct(from, to)
#2.2 计算位移
tmp_obj1<-plot_data2 %>% 
    group_by(from) %>% 
    mutate(V1=1/n()) %>% 
    group_by(from) %>% 
    mutate(from_end = cumsum(V1), 
           from_start = from_end-V1) %>%
    select(from, to, from_start, from_end)
tmp_obj2<-plot_data2 %>% 
    group_by(to) %>% 
    mutate(V1=1/n()) %>% 
    group_by(to) %>% 
    mutate(to_end = cumsum(V1), 
           to_start = to_end-V1) %>%
    select(from, to, to_start, to_end)
plot_data2 %<>% left_join(tmp_obj1) %>% left_join(tmp_obj2)
# 2.3 合并上述两表
plot_data2 %<>% 
  left_join(plot_data3, by=c('from'='id')) %>% 
  rename('from_position'=position, 'from_sector'=sector) %>%
  left_join(plot_data3, by=c('to'='id')) %>%
  rename('to_position'=position, 'to_sector'=sector)

由于上述plot_data2包含了全部点之间的连线关系,我们可能不需要这么多,所以可以挑选自己想要展示的数据。这一步可能需要反复画图几次确定需要展示的数据

#3. 进一步筛选想要展示的连线
# highlight the pathway I wanted
highlight_pathway <- c("pathway1", "pathway2")
highlight_KO <- c("KO5", "KO6", "KO20")
highlight_gene <-  c("gene292", "gene256", "gene67", "gene146", "gene52", "gene391", "gene139", "gene327", "gene218", "gene142", "gene375", "gene194")

plot_link <- plot_data2 %>% filter(to %in% c(highlight_pathway, highlight_KO)) %>% mutate(col="#dcdcdc80")
highlight_link <- plot_link %>% filter (from %in% c(highlight_gene, highlight_KO)) %>% mutate(col="#fb9b9a80")
plot_link <- plot_link %>% filter (!from %in% c(highlight_gene, highlight_KO))
plot_data2<-rbind(plot_link, highlight_link)

使用circos.link循环作图

for ( idx in seq(nrow(plot_data2))){
  tmp_obj <- plot_data2[idx,]
  circos.link(tmp_obj[['from_sector']], 
              c(tmp_obj[['from_position']] + tmp_obj[['from_start']], 
                tmp_obj[['from_position']] + tmp_obj[['from_end']]), 
              tmp_obj[['to_sector']], 
              c(tmp_obj[['to_position']] + tmp_obj[['to_start']], 
                tmp_obj[['to_position']] + tmp_obj[['to_end']]),
              col = tmp_obj[['col']],
              border = NA
              )
}

全部代码

library(tidyverse)
library(magrittr)
library(circlize)
#模拟数据
## Data1
fpkm <- rbind(cbind(matrix(rnorm(500*3, mean = 1), nr = 500), 
                   matrix(rnorm(500*3, mean = 2), nr = 500),
                   matrix(rnorm(500*3, mean = 3), nr = 500)))
fpkm <- fpkm[sample(500, 500), ] # randomly permute rows
rownames(fpkm) <- paste0("gene", seq(500))
colnames(fpkm) <- c("A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3")
fpkm %<>% as.data.frame() %>% mutate(gene = row.names(.))
# Data2
pathways <- rep(paste0("pathway", seq(6)),  sample(12:20, size = 6)) %>% sample(70)
KOs <- rep(paste0("KO", seq(20)),  sample(5:20, size = 20, replace = TRUE)) %>% sample(70)
KOannotation <- data.frame(KO=KOs, pathway=pathways)
KOannotation <- KOannotation[sample(70, 200, TRUE),]
KOannotation$gene <- sample(paste0("gene", seq(500)),200)
             
# 假设你有富集到的想要可视化的通路
maps <- c("pathway1", "pathway2", "pathway3")
samples <- c("A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3")
# 准备热图颜色
col_fun1 = colorRamp2(c(-2, 0, 2), c("#247ab5", "white", "#fda1a0"))
# 需要画图的基因
plot_gene <- KOannotation %>% 
  filter(pathway %in% maps) %>% 
  pull(gene) %>%
  {filter(fpkm, row.names(fpkm) %in% .)}
# 需要画图的KO
plot_KO <- plot_gene %>% 
  left_join(KOannotation) %>%
  filter(pathway %in% maps) %>% # There are some unenriched map
  group_by(KO) %>%
  summarise(across(samples,sum))
# 需要画图的Pathway
plot_map <- plot_gene %>% 
    left_join(KOannotation) %>%
    filter(pathway %in% maps) %>% # There are some unenriched map
    group_by(pathway) %>%
    summarise(across(samples,sum))

plot_data1 <- bind_rows(plot_gene %>% rename(id=gene), 
                        plot_KO %>% rename(id=KO), 
                        plot_map %>% rename(id=pathway)) %>%
    `row.names<-`(.$id) %>%
    select(-id)
plot_data1 <- t(scale(t(plot_data1))) %>% as.data.frame()
# 在热图上划分为gene、KO、pathway
lev_split = row.names(plot_data1) %>% str_match("[a-zA-Z]+") %>% factor()
circos.clear()
circos.par(gap.degree=10, track.height=0.1)
# 分多次画表达谱数据,更有层次
circos.heatmap(plot_data1[samples[1:3]], split = lev_split , col = col_fun1, rownames.side = "outside", cluster = TRUE)
circos.heatmap(plot_data1[samples[7:9]], split = lev_split , col = col_fun1)
circos.heatmap(plot_data1[samples[4:6]], split = lev_split , col = col_fun1)
plot_data3 = data.frame()
for(lev in levels(lev_split)){
  a <- rownames(plot_data1)[lev_split==lev][get.cell.meta.data("row_order", sector.index = lev)]
  a <- seq(length(a)) %>% `names<-`(a) %>% enframe("id", "position")
  a$sector = lev
  plot_data3 <- rbind(plot_data3, a)
} 
plot_data3$position <- plot_data3$position - 1 # 因为每个元素的范围是0~1,所以统一减一方便后面相加
#2.1 获取点对点连接表
plot_data2 <- KOannotation %>%
    filter(gene %in% row.names(plot_data1), KO %in% row.names(plot_data1)) %>%
    select(gene, KO) %>%
    rename(from=gene, to=KO)
tmp_obj1 <- KOannotation %>%
    filter(KO %in% row.names(plot_data1), pathway %in% row.names(plot_data1)) %>%
    select(KO, pathway) %>%
    rename(from=KO, to=pathway)
plot_data2 %<>% bind_rows(tmp_obj1) %>% distinct(from, to)
#2.2 计算位移
tmp_obj1<-plot_data2 %>% 
    group_by(from) %>% 
    mutate(V1=1/n()) %>% 
    group_by(from) %>% 
    mutate(from_end = cumsum(V1), 
           from_start = from_end-V1) %>%
    select(from, to, from_start, from_end)
tmp_obj2<-plot_data2 %>% 
    group_by(to) %>% 
    mutate(V1=1/n()) %>% 
    group_by(to) %>% 
    mutate(to_end = cumsum(V1), 
           to_start = to_end-V1) %>%
    select(from, to, to_start, to_end)
plot_data2 %<>% left_join(tmp_obj1) %>% left_join(tmp_obj2)
# 2.3 合并上述两表
plot_data2 %<>% 
  left_join(plot_data3, by=c('from'='id')) %>% 
  rename('from_position'=position, 'from_sector'=sector) %>%
  left_join(plot_data3, by=c('to'='id')) %>%
  rename('to_position'=position, 'to_sector'=sector)
highlight_pathway <- c("pathway1", "pathway2")
highlight_KO <- c("KO5", "KO6", "KO20")
highlight_gene <-  c("gene292", "gene256", "gene67", "gene146", "gene52", "gene391", "gene139", "gene327", "gene218", "gene142", "gene375", "gene194")

plot_link <- plot_data2 %>% filter(to %in% c(highlight_pathway, highlight_KO)) %>% mutate(col="#dcdcdc80")
highlight_link <- plot_link %>% filter (from %in% c(highlight_gene, highlight_KO)) %>% mutate(col="#fb9b9a80")
plot_link <- plot_link %>% filter (!from %in% c(highlight_gene, highlight_KO))
plot_data2<-rbind(plot_link, highlight_link)
for ( idx in seq(nrow(plot_data2))){
  tmp_obj <- plot_data2[idx,]
  circos.link(tmp_obj[['from_sector']], 
              c(tmp_obj[['from_position']] + tmp_obj[['from_start']], 
                tmp_obj[['from_position']] + tmp_obj[['from_end']]), 
              tmp_obj[['to_sector']], 
              c(tmp_obj[['to_position']] + tmp_obj[['to_start']], 
                tmp_obj[['to_position']] + tmp_obj[['to_end']]),
              col = tmp_obj[['col']],
              border = NA
              )
}

成品

注意点

  1. 如果画多层圈图,会根据行号对齐,但是不会根据行名对齐。所以所有组的表达谱数据必须一步准备。

  2. 热图和和弦图代码之间进行了大量数据清洗和计算,不要怕,没关系。因为只有拿到热图之后才能获取对应gene、KO、pathway的位置

  3. 每次画图之前习惯性的用circos.clear()清理一下缓存。

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值