R circlize - 绘图边距和绘图区域

6
我正在使用r库“circlize”尝试复制该网站的图表https://gjabel.wordpress.com/2014/03/28/circular-migration-flow-plots-in-r/。不幸的是,我遇到了两个问题:
首先,对于所有绘图区域(即“Mexico”,轨道“1”中的Note: 1 point is out of plotting region in sector 'Mexico', track '1'),我都收到了警告。我认为问题出在circos.textcircos.axis中,因为我在使用它们时同时使用了被弃用的函数direction。但是即使使用facing代替direction,问题仍然存在。所以我猜我没有理解这个警告的含义。您有什么提示可以帮助我吗?
此外,在我的图中,链接与部分之间的距离非常远。因此,我尝试减少轨道边缘,这解决了问题,但现在段的名称超出了边缘,我无法将其可视化。有更好的方法来解决这个问题吗?
这是我到目前为止写的内容(几乎完全取自此网站https://github.com/null2/globalmigration)。
library("circlize")
library("plyr")
library("migest")
#load data
m<-read.table(system.file("science", "country_custom.txt", package = "migest"), skip=2, stringsAsFactors=FALSE)
#1)a data.frame to store information on each segment of the circle to be plotted
df1<-m[,1:3]
names(df1)<-c("order","rgb","region")
df1$region<-gsub("\\.", "\n", df1$region)
#2) a matrix containing the flow data (in this example only 28 countries)
m<-m[,-(1:3)]/1e05
m<-as.matrix(m)
dimnames(m)<-list(orig=df1$region,dest=df1$region)
#sort order of data.frame and matrix for plotting in circos    
df1<-arrange(df1, order) #reordering a data frame by its columns
df1$region <- factor(df1$region, levels=df1$region)
m<-m[levels(df1$region),levels(df1$region)]
#define ranges of circos sectors and their colors (both of the sectors and the links)
#determine the length of segments on the outside of the plot.
df1$xmin <- 0
df1$xmax <- rowSums(m)+colSums(m) #inflows+outflows
#set the colour names for segments and flows
n<-nrow(df1)
df1 <- cbind(df1, matrix(as.numeric(unlist(strsplit(df1$rgb,","))),nrow=n, byrow=TRUE) )
names(df1)[ncol(df1)-2:0]<-c("r","g","b")
df1$rcol<-rgb(df1$r, df1$g, df1$b, max = 255)
df1$lcol<-rgb(df1$r, df1$g, df1$b, alpha=200, max = 255)
##plot sectors
windows()
par(mar=rep(0,4))
circos.clear()
#1)basic circos graphic parameters
circos.par(cell.padding=c(0,0,0,0), track.margin=c(0,0.01), start.degree = 90, gap.degree =4)
#2)sector details
circos.initialize(factors = df1$region, xlim = cbind(df1$xmin, df1$xmax))
#3)plot sectors
circos.trackPlotRegion(ylim = c(0, 1), factors = df1$region, track.height=0.1,
       panel.fun = function(x, y) {
         #select details of current sector
         name = get.cell.meta.data("sector.index")
         i = get.cell.meta.data("sector.numeric.index")
         xlim = get.cell.meta.data("xlim")
         ylim = get.cell.meta.data("ylim")

         #text direction (dd) and adjusmtents (aa)
         theta = circlize(mean(xlim), 1.3)[1, 1] %% 360
         dd <- ifelse(theta < 90 || theta > 270, "vertical_right", "vertical_left")
         aa = c(1, 0.5)
         if(theta < 90 || theta > 270)  aa =c(0, 0.5)

         #plot country labels
         circos.text(x=mean(xlim), y=1.7, labels=name, direction = dd,
         cex=0.6,adj = aa)
         #circos.text(x=mean(xlim), y=2, labels=name, facing = "bending",cex=0.6)             

         #plot main sector
         circos.rect(xleft=xlim[1], ybottom=ylim[1], xright=xlim[2], ytop=ylim[2], 
         col = df1$rcol[i], border=df1$rcol[i])

         #blank in part of main sector
         circos.rect(xleft=xlim[1], ybottom=ylim[1], xright=xlim[2]-rowSums(m)[i], ytop=ylim[1]+0.3, 
         col = "white", border = "white")

         #white line all the way around
         circos.rect(xleft=xlim[1], ybottom=0.3, xright=xlim[2], ytop=0.32, col = "white", border = "white")

         #plot axis
         #NOTE: Ticks indicate the number of migrants in 100s.
         circos.axis(labels.cex=0.6, direction = "outside", major.at=seq(from=0,to=floor(df1$xmax)[i],by=5), 
         minor.ticks=1, labels.away.percentage = 0.15)
       })

非常感谢您的帮助。
编辑: 我添加了脚本的第二部分,因为似乎需要解决第二个问题。
#plot links
#create a new dataframe containing the long form of the matrix m
#add sum values to df1, marking the x-position of the first links out (sum1) and in (sum2). Updated for further links in loop below.
df1$sum1 <- colSums(m) #outflows
df1$sum2 <- numeric(n)
#create a data.frame of the flow matrix sorted by flow size, to allow largest flow plotted first
df2<-cbind(as.data.frame(m),orig=rownames(m),  stringsAsFactors=FALSE)
#long matrix
df2<-reshape(df2, idvar="orig", varying=list(1:n), direction="long", timevar="dest", time=rownames(m),  v.names = "m") 
df2<-arrange(df2,desc(m))
#keep only the largest flows to avoid clutter
df2<-subset(df2, m>quantile(m,0.925))
#plot links
for(k in 1:nrow(df2)){
require("circlize")
#i,j reference of flow matrix
#note: you are selecting the states in region1 according to the edgelist in region2
i<-match(df2$orig[k],df1$region)
j<-match(df2$dest[k],df1$region)
#plot link
#sector.index1=sender
#point1=size of the base of the link at the origin
#We set the origin segment to start at the current sum of outflows from the sender country (df1$sum1[i])    
#We set the end of the segment outflow equal to the total outflows from the sender country, plus the flow from the edge we considered
circos.link(sector.index1=df1$region[i], point1=c(df1$sum1[i], df1$sum1[i] + abs(m[i, j])),
sector.index2=df1$region[j], point2=c(df1$sum2[j], df1$sum2[j] + abs(m[i, j])),
col = df1$lcol[i]) #, top.ratio==0.66, top.ratio.low==0.67)
#note: The height and thickness of the link at its mid-point is determined by the top.ratio and the top.ratio.low argument
#update sum1 and sum2 for use when plotting the next link
df1$sum1[i] = df1$sum1[i] + abs(m[i, j])
df1$sum2[j] = df1$sum2[j] + abs(m[i, j])
}

看起来很多代码与问题无关。如果您能将其减少到一个小的可重现的示例,例如通过删除所有准备“df1”的代码,并包括“dput(df1)”的输出,那将是非常好的。 - jbaums
1
嗨jbaums,感谢您的评论。我编辑了帖子中的脚本,现在它应该更加专注。现在只涉及片段部分,因为链接不是必要的。至于可重复性,我知道dput()通常是一个很好的解决方案,但在这种情况下,它需要超过50行,而我只使用了25行。但如果您认为更好,我可以改变它。 - sezonzo
抱歉 - 我没有意识到您正在加载包中包含的文件。干杯。 - jbaums
这篇博客展示了如何使用相同的库file.show(system.file("demo/cfplot_reg.R", package = "migest"))查看图形的代码。或者你已经知道了吗? - nacnudus
请在您在 SO 上发布的代码中不要放置 rm(list = ls())。我已将其删除(可以将其注释掉)。 - Henrik
3个回答

7
您可以在当前版本的circlize中查看chordDiagram()chordDiagram()是一个灵活的函数,可制作这样的弦图(即带有内部链接的圆形图)。
该函数是在最近版本的circlize中引入的,您不需要太多代码来自定义一个弦图。该软件包还附带了一个文档,详细介绍如何制作简单或复杂的弦图。
下面是一个快速演示,以重现您感兴趣的图形:
mat = matrix(runif(36), 6)
rownames(mat) = letters[1:6]
colnames(mat) = letters[1:6]

library(circlize)
circos.par(gap.degree = 8)
chordDiagram(mat, grid.col = 1:6, directional = TRUE, annotationTrack = "grid",
  preAllocateTracks = list(list(track.height = 0.05),
                           list(track.height = 0.05)))
circos.trackPlotRegion(track.index = 1, panel.fun = function(x, y) {
  xlim = get.cell.meta.data("xlim")
  ylim = get.cell.meta.data("ylim")
  sector.index = get.cell.meta.data("sector.index")
  circos.text(mean(xlim), mean(ylim), sector.index, facing = "inside", niceFacing = TRUE)
}, bg.border = NA)
circos.trackPlotRegion(track.index = 2, panel.fun = function(x, y) {
  circos.axis("bottom", major.tick.percentage = 0.2, labels.cex = 0.4)
}, bg.border = NA)
circos.clear()

enter image description here


6
第一个关于笔记信息的问题并不是真正的问题。无论是否有警告,您仍然可以获得所需的情节。您可以使用以下方法来抑制消息:
circos.par(points.overflow.warning=FALSE)

第二个问题是最近更新circlize包的结果。您需要通过添加来更改轨道边距

,以使它们从初始设置更改。
circos.par(track.margin=c(0,0)) 

for循环中,在用circos.link函数绘制链接之前,使用circos.trackPlotRegion命令。

对于问题感到抱歉。我一直打算更新migest包中的演示文件,但在假期期间有些忘记了。


嗨,gjabel,非常感谢你在分享你的工作方面所做的所有努力。我编辑了这篇文章,以便每个人都可以参与我们的讨论,还可以阅读脚本的链接部分。 我尝试使用circos.par,但可能我并没有真正理解如何管理它,因为我的结果没有改变。你能否指出我应该如何精确地插入这个脚本? - sezonzo
@sezonzo,你只需要将上面答案中的那一行添加到你的脚本中(在循环之前)。完整的工作脚本现在已经放在我的Github页面上:https://github.com/gjabel/migest/blob/master/demo/cfplot_nat.R - guyabel

1

正如作者在这里所解释的那样

"points.overflow.warning"由于每个单元格实际上不是一个真正的绘图区域而仅仅是一个普通的矩形,因此它不会消除绘制在区域外的点。因此,如果一些点在绘图区域外,circlize将继续绘制点并打印警告。在某些情况下,在绘图区域外绘制一些图例或文本是有用的。将此值设置为FALSE以关闭警告。

通过canvas.xlimcanvas.ylim(在上述同一页中描述),您可以设置画布以避免或忽略警告。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接