R數據可視化20:弦圖

終于超過1k粉絲啦~之前說要小小慶祝一下,害怕直接放文章里會被封文章,所以福利大家可以見評論。前幾周忙著答辯畢業事宜沒有時間更新,雖然這幾天也挺忙哈哈哈哈,不過還是要定期更新的。今天我們來講一下弦圖的繪制。(上一次畫這個圖要以年為時間單位計算了?)

什么是弦圖(Chord Diagram)

弦圖是一種展示數據之間相互關系的圖形。弦圖中的數據點以圓的形式呈放射狀排列,并用線條來展示數據之間的聯系。在弦圖中,我們可以通過顏色和線條的粗細來展現不同類型聯系和強度。這種聯系有多種形式比如相關性,比如存在與否,比如遷入遷出等。

弦圖是一種美學上令人愉悅的展現方式,換句話說,可以提升你文章或者報告的水準,讓人有一種高大上的感覺。那么讓我們先來看幾個弦圖的例子。

弦圖的例子
使用弦圖展示不同OTU在不同環境中的存在情況

當然除了表示相關性弦圖也可以用于表示存在的情況。我們以這篇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)
})

1960-1965年的人口遷移

然后我們可以寫一個循環生成多張圖然后制作成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")
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: 火山圖

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373