rcirclize

Add percentage labels inside bars in circos.barplot in Circlize


I was able to create a stacked bar chart like this:

library(circlize)
library(vegan) # for `reorder.hclust` (may be masked by the library `seriation`)
library(dendextend) # for `color_branches`

t=read.table(text="Kalmyk 0.119357 0.725057 0.000010 0.037803 0.117774
Kyrgyz_China 0.039367 0.512079 0.230150 0.095038 0.123366
Altaian_Chelkan 0.034095 0.000010 0.919478 0.000010 0.046407
Azeri 0.051638 0.004671 0.010727 0.902646 0.030318
Uzbek 0.102725 0.273261 0.001854 0.452126 0.170033
Salar 0.000010 0.539636 0.460334 0.000010 0.000010
Tatar_Kazan 0.113456 0.057026 0.000010 0.099336 0.730171
Tatar_Siberian 0.251376 0.221389 0.000010 0.077505 0.449721
Finnish 0.007214 0.000010 0.000010 0.015174 0.977592
Yakut 0.505434 0.473202 0.000010 0.002914 0.018440
Mansi 0.572791 0.000010 0.000010 0.000010 0.427179
Altaian 0.222424 0.335614 0.358801 0.032694 0.050468
Shor_Mountain 0.233984 0.000010 0.724596 0.000010 0.041400
Chuvash 0.180171 0.011056 0.000010 0.006462 0.802301
Enets 0.920409 0.000010 0.000010 0.000010 0.079561
Yukagir_Tundra 0.710359 0.289611 0.000010 0.000011 0.000010
Kyrgyz_Tajikistan 0.104000 0.563708 0.000010 0.125799 0.206483
Khakass_Kachin 0.254253 0.416760 0.174200 0.005262 0.149525
Tuvinian 0.448940 0.448899 0.000010 0.031803 0.070348
Besermyan 0.209841 0.001487 0.000010 0.000460 0.788202
Nogai_Astrakhan 0.062497 0.463590 0.000010 0.183203 0.290701
Todzin 0.725173 0.257670 0.000010 0.005836 0.011312
Kazakh 0.067027 0.518213 0.087979 0.114550 0.212231
Tofalar 0.815599 0.110299 0.000010 0.009693 0.064398
Karakalpak 0.009983 0.316964 0.389103 0.158275 0.125676
Estonian 0.000010 0.000010 0.000010 0.004409 0.995561
Dolgan 0.694025 0.255361 0.000010 0.049624 0.000979
Tatar_Siberian_Zabolotniye 0.521637 0.020132 0.000010 0.000010 0.458212
Uyghur 0.043578 0.486742 0.000010 0.318983 0.150687
Udmurt 0.256391 0.000010 0.001010 0.000010 0.742579
Evenk_FarEast 0.241328 0.606202 0.000010 0.000010 0.152451
Selkup 0.804662 0.000010 0.000010 0.000010 0.195308
Kumyk 0.060751 0.000112 0.000010 0.823905 0.115222
Hungarian 0.000010 0.000010 0.000010 0.244311 0.755659
Tubalar 0.159517 0.009457 0.802778 0.000010 0.028238
Turkmen 0.123631 0.226543 0.000010 0.529793 0.120023
Karelian 0.012854 0.000010 0.000010 0.000010 0.987116
Kazakh_China 0.074285 0.573009 0.152931 0.069362 0.130412
Mongol 0.033174 0.847004 0.025135 0.005178 0.089509
Daur 0.000010 0.995215 0.000010 0.000010 0.004755
Evenk_Transbaikal 0.611414 0.388556 0.000010 0.000010 0.000010
Nogai_Karachay_Cherkessia 0.119988 0.120774 0.000010 0.617261 0.141967
Veps 0.026887 0.000010 0.000010 0.000010 0.973083
Even 0.441349 0.278457 0.000010 0.015239 0.264946
Nganasan 0.999960 0.000010 0.000010 0.000010 0.000010
Bashkir 0.114088 0.056493 0.251488 0.030390 0.547542
Xibo 0.000010 0.985541 0.000010 0.000362 0.014077
Khakass 0.202707 0.171413 0.530905 0.007675 0.087300
Shor_Khakassia 0.258218 0.000010 0.694889 0.000010 0.046873
Nanai 0.105903 0.894067 0.000010 0.000010 0.000010
Buryat 0.064420 0.848458 0.017066 0.001696 0.068360
Yukagir_Forest 0.379416 0.096266 0.000010 0.003580 0.520728
Karachai 0.067138 0.004534 0.000010 0.798982 0.129336
Mordovian 0.022303 0.001193 0.000010 0.025251 0.951243
Turkish_Balikesir 0.092314 0.038550 0.000010 0.804964 0.064163
Turkish 0.040918 0.012255 0.000010 0.873179 0.073639
Kyrgyz_Kyrgyzstan 0.090129 0.607265 0.000010 0.122885 0.179711
Balkar 0.075115 0.000010 0.000010 0.829730 0.095136
Gagauz 0.000010 0.027887 0.015891 0.601619 0.354593
Nogai_Stavropol 0.070584 0.403817 0.000010 0.244701 0.280888
Negidal 0.248518 0.751452 0.000010 0.000010 0.000010
Tatar_Mishar 0.066112 0.037441 0.010377 0.138008 0.748062",row.names=1)

hc=reorder(hclust(dist(t)),-as.matrix(t)%*%seq(ncol(t))^2)

labelcolor=hcl(c(260,90,120,60,0,210,180,310)+15,60,70)
barcolor=hcl(c(310,260,120,60,210)+15,60,70)

labels=hc$labels[hc$order]
cut=cutree(hc,8)
dend=color_branches(as.dendrogram(hc),k=length(unique(cut)),
  col=labelcolor[unique(cut[labels])])

circos.clear()
png("a.png",w=2000,h=2000,res=200)
circos.par(cell.padding=c(0,0,0,0))
circos.initialize("a",xlim=c(0,nrow(t)))

circos.track(ylim=c(0,1),track.height=.2,track.margin=c(0,0),bg.border=NA,
  panel.fun=function(x,y)for(i in 1:nrow(t))circos.text(i-.5,0,labels[i],adj=c(0,.5),
    facing="clockwise",niceFacing=T,cex=.75,col=labelcolor[cut[labels[i]]]))

circos.track(ylim=c(0,1),track.height=.3,track.margin=c(0,.01),bg.border=NA,
  panel.fun=function(x,y)circos.barplot(as.matrix(t)[hc$order,],-.5+1:nrow(t),
    col=barcolor,bar_width=1,lwd=.3,border="gray20"))

circos.track(ylim=c(0,attr(dend,"height")),track.height=.4,track.margin=c(0,0),
  bg.border=NA,panel.fun=function(x,y)circos.dendrogram(dend))

circos.clear()
dev.off()

How can I additionally add text for the percentage of each bar in the middle of the bar? I'd like to display the percentages as rounded to an integer, with percentages of 0 or 1 omitted.


Solution

  • I figured it out by modifying the code of the circos.barplot function.

    The labels are drawn in a second loop after the rectangles so that the rectangles are not drawn over the labels.

    library(circlize)
    library(vegan) # for reorder.hclust (may be masked by `seriation`)
    library(dendextend) # for color_branches
    
    t=read.table(text="Kalmyk 0.119357 0.725057 0.000010 0.037803 0.117774
    Kyrgyz_China 0.039367 0.512079 0.230150 0.095038 0.123366
    Altaian_Chelkan 0.034095 0.000010 0.919478 0.000010 0.046407
    Azeri 0.051638 0.004671 0.010727 0.902646 0.030318
    Uzbek 0.102725 0.273261 0.001854 0.452126 0.170033
    Salar 0.000010 0.539636 0.460334 0.000010 0.000010
    Tatar_Kazan 0.113456 0.057026 0.000010 0.099336 0.730171
    Tatar_Siberian 0.251376 0.221389 0.000010 0.077505 0.449721
    Finnish 0.007214 0.000010 0.000010 0.015174 0.977592
    Yakut 0.505434 0.473202 0.000010 0.002914 0.018440
    Mansi 0.572791 0.000010 0.000010 0.000010 0.427179
    Altaian 0.222424 0.335614 0.358801 0.032694 0.050468
    Shor_Mountain 0.233984 0.000010 0.724596 0.000010 0.041400
    Chuvash 0.180171 0.011056 0.000010 0.006462 0.802301
    Enets 0.920409 0.000010 0.000010 0.000010 0.079561
    Yukagir_Tundra 0.710359 0.289611 0.000010 0.000011 0.000010
    Kyrgyz_Tajikistan 0.104000 0.563708 0.000010 0.125799 0.206483
    Khakass_Kachin 0.254253 0.416760 0.174200 0.005262 0.149525
    Tuvinian 0.448940 0.448899 0.000010 0.031803 0.070348
    Besermyan 0.209841 0.001487 0.000010 0.000460 0.788202
    Nogai_Astrakhan 0.062497 0.463590 0.000010 0.183203 0.290701
    Todzin 0.725173 0.257670 0.000010 0.005836 0.011312
    Kazakh 0.067027 0.518213 0.087979 0.114550 0.212231
    Tofalar 0.815599 0.110299 0.000010 0.009693 0.064398
    Karakalpak 0.009983 0.316964 0.389103 0.158275 0.125676
    Estonian 0.000010 0.000010 0.000010 0.004409 0.995561
    Dolgan 0.694025 0.255361 0.000010 0.049624 0.000979
    Tatar_Siberian_Zabolotniye 0.521637 0.020132 0.000010 0.000010 0.458212
    Uyghur 0.043578 0.486742 0.000010 0.318983 0.150687
    Udmurt 0.256391 0.000010 0.001010 0.000010 0.742579
    Evenk_FarEast 0.241328 0.606202 0.000010 0.000010 0.152451
    Selkup 0.804662 0.000010 0.000010 0.000010 0.195308
    Kumyk 0.060751 0.000112 0.000010 0.823905 0.115222
    Hungarian 0.000010 0.000010 0.000010 0.244311 0.755659
    Tubalar 0.159517 0.009457 0.802778 0.000010 0.028238
    Turkmen 0.123631 0.226543 0.000010 0.529793 0.120023
    Karelian 0.012854 0.000010 0.000010 0.000010 0.987116
    Kazakh_China 0.074285 0.573009 0.152931 0.069362 0.130412
    Mongol 0.033174 0.847004 0.025135 0.005178 0.089509
    Daur 0.000010 0.995215 0.000010 0.000010 0.004755
    Evenk_Transbaikal 0.611414 0.388556 0.000010 0.000010 0.000010
    Nogai_Karachay_Cherkessia 0.119988 0.120774 0.000010 0.617261 0.141967
    Veps 0.026887 0.000010 0.000010 0.000010 0.973083
    Even 0.441349 0.278457 0.000010 0.015239 0.264946
    Nganasan 0.999960 0.000010 0.000010 0.000010 0.000010
    Bashkir 0.114088 0.056493 0.251488 0.030390 0.547542
    Xibo 0.000010 0.985541 0.000010 0.000362 0.014077
    Khakass 0.202707 0.171413 0.530905 0.007675 0.087300
    Shor_Khakassia 0.258218 0.000010 0.694889 0.000010 0.046873
    Nanai 0.105903 0.894067 0.000010 0.000010 0.000010
    Buryat 0.064420 0.848458 0.017066 0.001696 0.068360
    Yukagir_Forest 0.379416 0.096266 0.000010 0.003580 0.520728
    Karachai 0.067138 0.004534 0.000010 0.798982 0.129336
    Mordovian 0.022303 0.001193 0.000010 0.025251 0.951243
    Turkish_Balikesir 0.092314 0.038550 0.000010 0.804964 0.064163
    Turkish 0.040918 0.012255 0.000010 0.873179 0.073639
    Kyrgyz_Kyrgyzstan 0.090129 0.607265 0.000010 0.122885 0.179711
    Balkar 0.075115 0.000010 0.000010 0.829730 0.095136
    Gagauz 0.000010 0.027887 0.015891 0.601619 0.354593
    Nogai_Stavropol 0.070584 0.403817 0.000010 0.244701 0.280888
    Negidal 0.248518 0.751452 0.000010 0.000010 0.000010
    Tatar_Mishar 0.066112 0.037441 0.010377 0.138008 0.748062",row.names=1)
    
    hc=hclust(dist(t))
    hc=reorder(hc,-(t[,1]+t[,2]-t[,4]-2*t[,5]))
    
    labelcolor=hcl(c(260,90,120,60,0,210,180,310)+15,60,70)
    barcolor=hcl(c(310,260,120,60,210)+15,60,70)
    
    labels=hc$labels[hc$order]
    cut=cutree(hc,8)
    dend=color_branches(as.dendrogram(hc),k=length(unique(cut)),col=labelcolor[unique(cut[labels])])
    t=t[hc$labels[hc$order],]
    
    circos.clear()
    png("a.png",w=2500,h=2500,res=300)
    circos.par(cell.padding=c(0,0,0,0),gap.degree=5,points.overflow.warning=F)
    circos.initialize("a",xlim=c(0,nrow(t)))
    
    circos.track(ylim=c(0,1),bg.border=NA,track.height=.28,track.margin=c(.01,0),
      panel.fun=function(x,y)for(i in 1:nrow(t))circos.text(i-.5,0,labels[i],adj=c(0,.5),facing="clockwise",niceFacing=T,cex=.65,col=labelcolor[cut[labels[i]]]))
    
    circos.track(ylim=c(0,1),track.margin=c(0,0),track.height=.35,bg.lty=0,panel.fun=function(x,y){
      mat=as.matrix(t)
      pos=1:nrow(mat)-.5
      barwidth=1
      for(i in 1:ncol(mat)){
        seq1=rowSums(mat[,seq(i-1),drop=F])
        seq2=rowSums(mat[,seq(i),drop=F])
        circos.rect(pos-barwidth/2,if(i==1){0}else{seq1},pos+barwidth/2,seq2,col=barcolor[i],border="gray20",lwd=.1)
      }
      for(i in 1:ncol(mat)){
        seq1=rowSums(mat[,seq(i-1),drop=F])
        seq2=rowSums(mat[,seq(i),drop=F])
        lab=round(100*t[,i])
        lab[lab<=1]=""
        circos.text(pos,if(i==1){seq1/2}else{seq1+(seq2-seq1)/2},labels=lab,col="gray10",cex=.4,facing="downward")
      }
    })
    
    circos.track(ylim=c(0,attr(dend,"height")),bg.border=NA,track.margin=c(0,.0015),track.height=.35,panel.fun=function(x,y)circos.dendrogram(dend))
    
    circos.clear()
    dev.off()