R绘图(7): 把散点图的点换成扇形

2021-07-22

前几天分析了一批单细胞TCR的数据,需要画这样一个图:

行是不同的样本,列是不同的T细胞类型,每一个位置点的大小表示T细胞的数目多少,并且还需要根据T细胞所属的克隆型类型涂色。这个图用来描述TCR是我自己构思的,之前没有在文章中见过。好在画图工具是现成的,用的是余老师的scatterpie包。后台回复20210722即可获取本文的测试数据。

1. 导入测试数据

library(tidyverse)
library(scatterpie)
library(reshape2)

df1=read.table("test.txt",header = T,row.names = 1,sep = "\t",stringsAsFactors = F)

看一下数据格式

> head(df1)
sample celltype clonal_expansion
TTAGTTCGTTCGGCAC tumor5        E                1
ATAACGCGTGAGGCTA tumor5        A        morethan3
CTCTAATCAACACCTA tumor1        B                1
AGACGTTAGTGTGGCA tumor5        A                1
AGATCTGTCACGGTTA tumor5        C                1
TCAGCTCTCGGCGCAT tumor3        A                1

> table(df1$sample,df1$celltype)
A   B   C   D   E   F   G
tumor0  12  60   2   0  21  13   5
tumor1  42 225  65   6  75  59  19
tumor3  44  79  20   4  26  51   6
tumor5 283 134 209  97  23 334  86

> table(df1$clonal_expansion,df1$celltype)

A   B   C   D   E   F   G
1         230 485 212  92 128 364 107
2          68   8  28  12   9  54   6
morethan3  83   5  56   3   8  39   3

原始数据框有4个肿瘤样本,7个T细胞类型。
clonal_expansion表示这个T细胞所属的TCR克隆型总共有几个细胞(这几个细胞的TCR克隆型是一样的)

2. 数据转换

长数据转宽数据

df3=df1 %>% dcast(sample+celltype~clonal_expansion)

reshape2包的dcast函数可以将长数据转化为宽数据,
这一步之后,每一行表示在一定的sample、一定的celltype之下,不同的clonal_expansion分组各有多少个值
(默认的整合函数为求和计算)

> head(df3)
sample celltype  1 2 morethan3
1 tumor0        A 12 0         0
2 tumor0        B 60 0         0
3 tumor0        C  2 0         0
4 tumor0        E 19 2         0
5 tumor0        F 13 0         0
6 tumor0        G  5 0         0

除了已知的4个样本,7种类型,我还希望增加一个虚拟样本allsample,一个虚拟类型alltype,1乘4+1乘7+1乘1=12,即还需要人为添加12行

df4a=data.frame()
for (samplei in unique(df3$sample)) {
  tmp1=df3 %>% filter(sample == samplei)
  df4a=rbind(df4a,c(sample=samplei,celltype="alltype",colSums(tmp1[,3:5])))
}

df4b=data.frame()
for (typei in unique(df3$celltype)) {
  tmp2=df3 %>% filter(celltype == typei)
  df4b=rbind(df4b,c(sample="allsample",celltype=typei,colSums(tmp2[,3:5])))
}

df4c=data.frame()
df4c=c(sample="allsample",celltype="alltype",colSums(df3[,3:5]))

colnames(df4a)=colnames(df3)
colnames(df4b)=colnames(df3)
df4=rbind(df4a,df4b) %>% rbind(df4c)
df4$`1`=as.numeric(df4$`1`)
df4$`2`=as.numeric(df4$`2`)
df4$morethan3=as.numeric(df4$morethan3)
df5=df3 %>% rbind(df4)

3. 画图

貌似这个R包不支持字符,所以这里我将字符人为转换为数字

df5$sample=ifelse(df5$sample=="tumor0",-1,
                    ifelse(df5$sample=="tumor1",-2,
                           ifelse(df5$sample=="tumor3",-3,
                                  ifelse(df5$sample=="tumor5",-4,-5))))

df5$celltype=ifelse(df5$celltype=="A",1,
                      ifelse(df5$celltype=="B",2,
                             ifelse(df5$celltype=="C",3,
                                    ifelse(df5$celltype=="D",4,
                                           ifelse(df5$celltype=="E",5,
                                                  ifelse(df5$celltype=="F",6,
                                                         ifelse(df5$celltype=="G",7,8)))))))

设置配色

library(RColorBrewer)
library(scales)
color_ce=brewer.pal(9, "YlGnBu")[c(3,5,8)]
names(color_ce)=c("1","2","morethan3")

添加一列半径,用每一行细胞数的和映射

df5$radius=log2(rowSums(df5[,3:5])) / 25 #这个公式不唯一,保证细胞多的时候图形大就可以了

以下是画图主要代码:

ggplot() + geom_scatterpie(aes(x=celltype, y=sample, r=radius), data=df5,cols=colnames(df5)[3:5],color=NA) + #cols参数表示用那几列来画扇形
  geom_hline(yintercept = -4.5,linetype=5)+
  geom_vline(xintercept = 7.5,linetype=5)+
  geom_scatterpie_legend(df5$radius, x=3.5, y=-1.3,n=4,labeller = function(x) round(2^(x*25)))+ #n参数表示图例显示几个圆;labeller是一个函数,通过这个函数可以反推出真实数值
  coord_equal()+
  scale_fill_manual(values = color_ce)+
  scale_x_continuous("",expand = c(0,0),breaks = 1:8,labels = c("A","B","C","D","E","F","G","alltype"))+
  scale_y_continuous("",expand = c(0,0),breaks = -5:-1,labels = c("allsample","tumor5","tumor3","tumor1","tumor0"))+
  theme_bw()+
  theme(
    panel.grid = element_blank(),
    legend.title = element_blank()
  )
ggsave("clonal_expansion_pie.pdf",width = 20,height = 15,units = "cm")

之后就能得到封面那张图了。

因水平有限,有错误的地方,欢迎批评指正!