Chào các bạn, hôm nay Nhi xin giới thiệu với các bạn package visNetwork trong R. Công dụng của package này là vẽ những network tương tác, tức là sơ đồ network của bạn có tính “động” và người đọc có thể tác động lên nó như chơi với một chùm bong bóng.
Trong thí dụ minh họa này, Nhi sẽ vẽ network cho một vấn đề đơn giản nhất,đó là phân tích tương quan giữa 13 biến số và 1 biến số đích.
Những packages sẽ được dùng trong thí dụ này:
library(tidyverse)
## Loading tidyverse: ggplot2
## Loading tidyverse: tibble
## Loading tidyverse: tidyr
## Loading tidyverse: readr
## Loading tidyverse: purrr
## Loading tidyverse: dplyr
## Conflicts with tidy packages ----------------------------------------------
## filter(): dplyr, stats
## lag(): dplyr, stats
library(igraph)
##
## Attaching package: 'igraph'
## The following objects are masked from 'package:dplyr':
##
## as_data_frame, groups, union
## The following objects are masked from 'package:purrr':
##
## compose, simplify
## The following object is masked from 'package:tidyr':
##
## crossing
## The following object is masked from 'package:tibble':
##
## as_data_frame
## The following objects are masked from 'package:stats':
##
## decompose, spectrum
## The following object is masked from 'package:base':
##
## union
library(RColorBrewer)
library(visNetwork)
Đầu tiên Nhi tải dữ liệu và dọn dẹp nó 1 chút
df=read.table("https://ww2.amstat.org/publications/jse/datasets/fat.dat.txt")
df= df[,c(2, 5:7, 10:19)]
names(df)=c("brozek_fat","age","weight","height","neck","chest","abdomen","hip","thigh","knee","ankle","biceps","forearm","wrist")
df= df[-42,] #wrong value ?
head(df)
## brozek_fat age weight height neck chest abdomen hip thigh knee ankle
## 1 12.6 23 154.25 67.75 36.2 93.1 85.2 94.5 59.0 37.3 21.9
## 2 6.9 22 173.25 72.25 38.5 93.6 83.0 98.7 58.7 37.3 23.4
## 3 24.6 22 154.00 66.25 34.0 95.8 87.9 99.2 59.6 38.9 24.0
## 4 10.9 26 184.75 72.25 37.4 101.8 86.4 101.2 60.1 37.3 22.8
## 5 27.8 24 184.25 71.25 34.4 97.3 100.0 101.9 63.2 42.2 24.0
## 6 20.6 24 210.25 74.75 39.0 104.5 94.4 107.8 66.0 42.0 25.6
## biceps forearm wrist
## 1 32.0 27.4 17.1
## 2 30.5 28.9 18.2
## 3 28.8 25.2 16.6
## 4 32.4 29.4 18.2
## 5 32.2 27.7 17.7
## 6 35.7 30.6 18.8
Sau đó Nhi chuyển data thành matrix rồi dùng hàm scale() để chuẩn hóa nó.
dfscale=df%>%as.matrix()%>%scale()
m=as.matrix(cor(dfscale))
Chúng ta đã tạo ra 1 correlation matrix m
Nhắc lại một chút về graph và network: Đây là một dạng sơ đồ biểu diễn mối liên hệ (edges) giữa các phần tử (nodes). Như vậy matrix tương quan cũng có thể được xem như 1 network trong đó mỗi biến số lần lượt bắt cặp với chính nó (đường chéo matrix) và với mỗi biến số còn lại.
Trước hết Nhi loại bỏ đường chéo khỏi matrix. Sau đó chuyển matrix thành một dataframe, rồi lọc bỏ, chỉ giữ lại những liên hệ với 1 biến số đích là brozek_fat.
dataframe này được đưa vào package igraph để tạo ra 1 graph. Nhưng đây chỉ mới là network cơ bản, để package visNetwork có thể hiểu được nó, ta phải chuyển đổi từ igraph sang visNetwork bằng hàm toVisNetworkData
# From matrix to network
diag(m)<-0
cdf=data.frame(row=rownames(m)[row(m)[upper.tri(m)]],
col=colnames(m)[col(m)[upper.tri(m)]],
corr=m[upper.tri(m)])
names(cdf)=c("from","to","corr")
cdf=subset(cdf,cdf$from=="brozek_fat")
graph<-graph_from_data_frame(cdf)
vg = toVisNetworkData(graph)
Trong network của chúng ta có 13 edges và 13 nodes, nối với 1 node trung tâm là brozek_fat, do đó ta cần 13 màu khác nhau. Nhi có ý tưởng sẽ tô màu theo độ mạnh tương quan (-1 đến 1), nên sẽ tạo ra 13 màu liên tục từ xanh đến đỏ.
#Testing color
coledges=colorRampPalette(c("royalblue","red"))
plot(rep(1,13),col=(coledges(13)), pch=19,cex=3)
colnodes=colorRampPalette(c("#d3dfff","#ffd3d3"))
plot(rep(1,13),col=(colnodes(13)), pch=19,cex=3)
Cơ chế hoạt động của package visNetwork rất đơn giản, nó cần 2 dataframe, một cho nodes, 1 cho edges. Các thuộc tính của edges và nodes như hình dáng, máu sắc, kích thước… được quy định trong mỗi dataframe.
Ta tạo edges data trước, cái này hơi tế nhị, vì ta cần tô màu cho edges theo hệ số tương quan, và độ dài, độ dày của edges cũng sẽ tỉ lệ với hệ số tương quan.
edges = as.data.frame(vg$edges)%>%.[order(.$corr),]
edges=edges%>%mutate(.,label=round(.$corr,2),
length = (1-.$corr)*500,
width = .$corr*3,
color=coledges(13),
shadow = TRUE)
Sau đó ta làm đến nodes data, màu sắc của nodes cũng được tô theo hệ số tương quan.
nodes=data.frame(id=c(vg$nodes[1,1],edges$to),
label=c(vg$nodes[1,1],edges$to),
shape=c('circle',rep('ellipse',13)),
value=c(1,edges$corr),
color=c("gold",colnodes(13)),
shadow = TRUE)
Bây giờ ta có thể vẽ hình:
Các bạn có thể chơi đùa với network thỏa thích, như một chùm bong bóng.
visNetwork(nodes, edges,
main = "A correlation network") %>%
visInteraction(navigationButtons = TRUE)%>%
visNodes(physics=T)%>%
visEdges(physics=T,
arrows =list(from = list(enabled = TRUE, scaleFactor = 0.8)))
visNetwork là một công cụ rất vui, bạn có thể publish network của mình lên internet và dùng nó trong khi trình diễn Powerpoint. Bạn cũng có thể đặt nó trên 1 tablet và sử dụng khi thuyết trình poster, người xem có thể chơi với network của bạn.
Lợi thế của visnetwork là nó tương thích với igraph output. Tuy nhiên khả năng đồ họa của nó vẫn còn hạn chế. Thí dụ nó chưa làm được cluster analysis.
Network và graph không chỉ giới hạn trong các nghiên cứu OMICs, bạn có thể nhìn thấy ứng dụng của network khắp nơi trong cuộc sống hằng ngày. Phân tích tương quan cũng chính là 1 network.
Tạm biệt các bạn.