終于超過1k粉絲啦~之前說要小小慶祝一下,害怕直接放文章里會被封文章,所以福利大家可以見評論。前幾周忙著答辯畢業事宜沒有時間更新,雖然這幾天也挺忙哈哈哈哈,不過還是要定期更新的。今天我們來講一下弦圖的繪制。(上一次畫這個圖要以年為時間單位計算了?)
什么是弦圖(Chord Diagram)
弦圖是一種展示數據之間相互關系的圖形。弦圖中的數據點以圓的形式呈放射狀排列,并用線條來展示數據之間的聯系。在弦圖中,我們可以通過顏色和線條的粗細來展現不同類型聯系和強度。這種聯系有多種形式比如相關性,比如存在與否,比如遷入遷出等。
弦圖是一種美學上令人愉悅的展現方式,換句話說,可以提升你文章或者報告的水準,讓人有一種高大上的感覺。那么讓我們先來看幾個弦圖的例子。
當然除了表示相關性弦圖也可以用于表示存在的情況。我們以這篇A Deeper Look into the Biodiversity of the Extremely Acidic Copahue volcano-Río Agrio System in Neuquén, Argentina文獻為例,該研究探究了阿根廷一座火山的生物多樣性。
上面的弦圖就展現了不同的OTU在不同環境的存在情況。比如,研究發現OTU1存在于酸性礦山排水(AMD)、礦山(Mine)、河流(Riverine)、火山(Volcanic)等多個環境,其中在酸性礦山排水中的存在最多(連線最寬)。而關注不同的環境可以發現酸性礦山排水中主要存在OTU1、OTU5、OTU6、OTU7、OTU8、OTU20等微生物。
通常來說,當數據點不是很多的時候,弦圖能很直觀地展現出不同數據點之間的關系。但是當數據點過多的時候,可能弦圖看起來就有一些混亂了,不過具體是否采用這種圖,還是要看你想用圖去表達什么結論。
當然了多幅弦圖還能展現出不同組別或者不同時間點之間的差異,具體如何展現可以看我們今天的具體示例。
如何作弦圖
1)需要什么樣的數據
今天找到了一個酷炫的弦圖例子還是個動圖。該圖用來展示1960年到2015年的全球移民情況。當然我們會畫靜態圖+動圖。
我只是一個代碼搬運工,參考了國外網友寫的代碼:原代碼請點擊這里
我們要使用的數據來自于“migest”這個包。所以我們先安裝該包然后讀取數據。
我們要用的繪圖工具是來自“circlize”包的chordDiagram()函數。
首先我們來看一下數據的準備。數據具體分為2部分,一部分是用于作圖的具體移民數據,還有一部分是調整作圖參數的文件。
install.packages('migest')#安裝migest包
library(tidyverse)#使用該包提供的“read_csv()"功能
d0 <- read_csv(system.file("imr", "reg_flow.csv", package = "migest"))
d0
# A tibble: 891 x 4
year0 orig_reg dest_reg flow
<dbl> <chr> <chr> <dbl>
1 1960 Africa Africa 1377791
2 1960 Africa Eastern Asia 5952
3 1960 Africa Eastern Europe & Central Asia 7303
4 1960 Africa Europe 919252
5 1960 Africa Latin America & Caribbean 15796
6 1960 Africa Northern America 82463
7 1960 Africa Oceania 32825
8 1960 Africa Southern Asia 35603
9 1960 Africa Western Asia 106580
10 1960 Eastern Asia Africa 37301
# … with 881 more rows
可以看到該數據以5年為單位統計了不同地區的移民情況。實際上真正做弦圖只需要后三列,也就是從哪去哪去了多少。
下面我們再來看作圖參數的文件。migest包中也已經準備好了。
d1 <- read_csv(system.file("vidwp", "reg_plot.csv", package = "migest"))
d1
# A tibble: 9 x 5
region order1 col1 reg1 reg2
<chr> <dbl> <chr> <chr> <chr>
1 Northern America 1 #40A4D8 Northern America
2 Africa 2 #33BEB7 Africa NA
3 Europe 3 #B2C224 Europe NA
4 Eastern Europe & Central Asia 4 #FECC2F Eastern Europe & Central Asia
5 Western Asia 5 #FBA127 Western Asia
6 Southern Asia 6 #F66320 Southern Asia
7 Eastern Asia 7 #DB3937 Eastern Asia
8 Oceania 8 #A463D7 Oceania NA
9 Latin America & Caribbean 9 #0C5BCE Latin America & Caribbean
具體來說第一列就是地區的名字,第二列是順序,第三列是作圖所使用的顏色,第四和第五列大家可以猜猜看。
實際上,最后為了作圖效果好看,有部分地區的名字過長,所以我們會分為2行來展示,第四和第五列就是為了實現這個目的。
2)如何作圖
我們首先來做1960-1965年這段時間的圖:
library(circlize)
test<-d0[d0$year0==1960,-1]#篩選數據
chordDiagram(x = test,
directional = 1, #表示線條的方向,0代表沒有方向,1代表正向,-1代表反向,2代表雙向
order = d1$region,
grid.col = d1$col1, #顏色的設定
annotationTrack = "grid",#diy添加label和axis
transparency = 0.25,#線條的透明度
annotationTrackHeight = c(0.05, 0.1),#外面一圈的寬度
direction.type = c("diffHeight","arrows"), #線條是否帶有箭頭
link.arr.type = "big.arrow",#另一個選擇是巨丑無比的尖頭
diffHeight = -0.04#外圈和中間連線的間隔
)
# 添加labels and axis
circos.track(track.index = 1, bg.border = NA,
panel.fun = function(x, y) {
xlim = get.cell.meta.data("xlim")
sector.index = get.cell.meta.data("sector.index")
reg1 = d1 %>% filter(region == sector.index) %>% pull(reg1)
reg2 = d1 %>% filter(region == sector.index) %>% pull(reg2)
circos.text(x = mean(xlim), y = ifelse(is.na(reg2), 3, 4),labels = reg1, facing = "bending", cex =0.8)
circos.text(x = mean(xlim), y = 2.75, labels = reg2, facing = "bending", cex = 0.8)
circos.axis(h = "top", labels.cex = 0.6,labels.niceFacing = FALSE, labels.pos.adjust = FALSE)
})
然后我們可以寫一個循環生成多張圖然后制作成gif。我們可以根據時間點將數據切割。
library(tweenr)
d2 <- d0 %>%
mutate(corridor = paste(orig_reg, dest_reg, sep = " -> ")) %>%
select(corridor, year0, flow) %>%
mutate(ease = "linear") %>%
tween_elements(time = "year0", group = "corridor", ease = "ease", nframes = 10)
d2 <- d2 %>%
separate(col = .group, into = c("orig_reg", "dest_reg"), sep = " -> ") %>%
select(orig_reg, dest_reg, flow, everything())
d2$flow<-d2$flow/1e06
# create a directory to store the individual plots
dir.create("./plot-gif/")
library(circlize)
for(f in unique(d2$.frame)){
png(file = paste0("./plot-gif/globalchord", f, ".png"), height = 7, width = 7,
units = "in", res = 500)
# intialise the circos plot
circos.clear()
par(mar = rep(0, 4), cex=1)
circos.par(start.degree = 90, track.margin=c(-0.1, 0.1),
gap.degree = 4, points.overflow.warning = FALSE)
# plot the chord diagram
chordDiagram(x = d2[d2$.frame==f,1:3], directional = 1, order = d1$region,
grid.col = d1$col1, annotationTrack = "grid",
transparency = 0.25, annotationTrackHeight = c(0.05, 0.1),
direction.type = c("diffHeight", "arrows"), link.arr.type = "big.arrow",
diffHeight = -0.04, link.sort = TRUE, link.largest.ontop = TRUE)
# add labels and axis
circos.track(track.index = 1, bg.border = NA, panel.fun = function(x, y) {
xlim = get.cell.meta.data("xlim")
sector.index = get.cell.meta.data("sector.index")
reg1 = d1 %>% filter(region == sector.index) %>% pull(reg1)
reg2 = d1 %>% filter(region == sector.index) %>% pull(reg2)
circos.text(x = mean(xlim), y = ifelse(is.na(reg2), 3, 4),
labels = reg1, facing = "bending", cex = 1.1)
circos.text(x = mean(xlim), y = 2.75, labels = reg2, facing = "bending", cex = 1.1)
circos.axis(h = "top", labels.cex = 0.8,
labels.niceFacing = FALSE, labels.pos.adjust = FALSE)
})
# close plotting device
dev.off()
}
library(magick)
img <- image_read(path = "./plot-gif/globalchord0.png")
for(f in unique(d2$.frame)[-1]){
img0 <- image_read(path = paste0("./plot-gif/globalchord",f,".png"))
img <- c(img, img0)
message(f)
}
img1 <- image_scale(image = img, geometry = "720x720")
ani0 <- image_animate(image = img1, fps = 10)
image_write(image = ani0, path = "./globalchord.gif")
今天的分享就到這里啦。
往期R數據可視化分享
R數據可視化19: 環狀條形圖
R數據可視化18: 弧形圖
R數據可視化17: 桑基圖
R數據可視化16: 啞鈴圖
R數據可視化15: 傾斜圖 Slope Graph
R數據可視化14: 生存曲線圖
R數據可視化13: 瀑布圖/突變圖譜
R數據可視化12: 曼哈頓圖
R數據可視化11: 相關性圖
R數據可視化10: 蜜蜂圖 Beeswarm
R數據可視化9: 棒棒糖圖 Lollipop Chart
R數據可視化8: 金字塔圖和偏差圖
R數據可視化7: 氣泡圖 Bubble Plot
R數據可視化6: 面積圖 Area Chart
R數據可視化5: 熱圖 Heatmap
R數據可視化4: PCA和PCoA圖
R數據可視化3: 直方/條形圖
R數據可視化2: 箱形圖 Boxplot
R數據可視化1: 火山圖