美文网首页R ggplot
R 数据可视化 —— ggraph 边与节点

R 数据可视化 —— ggraph 边与节点

作者: 名本无名 | 来源:发表于2021-05-27 16:05 被阅读0次

    前言

    将网络图结构与我们平常绘制的数据图进行对比,我们可以发现,图的布局就相当于每个数据点的坐标,而节点就相当于数据点,边就是连接数据点的几何图形,可以是直线、曲线或带箭头的线等。

    所以,不同的节点形状和不同的边形状进行组合,就绘制出了上节我们展示的各式各样的图形。

    下面,我们要介绍节点与边的相关操作,图结构如下

    edges <- read.csv('~/Downloads/p53_signaling_pathway.csv')
    colnames(edges) <- c("from", "to")
    edges <- edges %>%
      mutate(corr = sample(-1:1, size = n(), replace = TRUE))
    
    nodes <- data.frame(
      name = unique(union(edges$from, edges$to))
      )
    nodes$type <- sample(c("up", "down"), size = length(nodes$name), replace = TRUE)
    
    g <- tbl_graph(nodes = nodes, edges = edges)
    

    节点

    节点就像是一个数据点,我们也可以用 ggplot2 的方式来绘制所有的节点

    ggraph(g, layout = 'kk') +
      geom_point(aes(x, y)) +
      theme_graph()
    

    因为对图进行布局时,会返回一个数据框

    > head(create_layout(g, layout = 'kk'))
                x           y   name friends .ggraph.orig_index circular .ggraph.index
    1  0.44575339 -2.14390304   MDM4     few                  1    FALSE             1
    2 -0.93225120  0.59792670  CHEK1     few                  2    FALSE             2
    3 -0.07559781  0.92801986    ATR     few                  3    FALSE             3
    4 -0.62547450  0.89958214  CHEK2     few                  4    FALSE             4
    5 -1.14085626  0.24507223    ATM     few                  5    FALSE             5
    6  2.63609895 -0.06153547 IGFBP3     few                  6    FALSE             6
    

    我们实际使用的是这个数据框来绘制的,因此,我们可以在 aes() 中使用布局算法返回的数据框中的变量

    虽然可以使用 ggplot2 所提供的几何图形,但是 ggraphggplot2 的基础上定义了自己的几何图形 geom_node_*(),让我们的代码更加清晰、简洁。

    ggraph 包含的节点几何图形有:

    1. geom_node_point

    将节点表示为点,其参数类似于 geom_point

    ggraph(g, layout = 'kk') +
      geom_node_point(aes(colour = type, shape = type), size = 4) +
      theme_graph()
    

    所有的 geom_node_*()函数都有一个额外的参数 filter 可以用于筛选节点

    ggraph(g, layout = 'kk') +
      geom_node_point(aes(filter = type == "down"), 
                      shape = 23, size = 4, 
                      colour = "blue", fill = "red") +
      theme_graph()
    
    1. geom_node_textgeom_node_label

    这两个函数用于将节点绘制成纯文本或带矩形框的文本,可以类比于 geom_textgeom_label,少了一个 group 参数,其他参数都一样

    ggraph(g, layout = 'stress') +
      geom_node_text(aes(label = name, colour = type), 
                     angle = 45, repel = TRUE) +
      theme_graph()
    
    ggraph(g, layout = 'stress') +
      geom_node_label(aes(label = name, fill = type), 
                      colour = "white", fontface = "bold", 
                      repel = TRUE) +
      theme_graph()
    

    使用 repel 尽可能避免文本的重叠

    1. geom_node_tile

    treemap 布局中,可以绘制矩形树状图。参数类似于 geom_tile()

    我们先构建一个树结构

    flareGraph <- tbl_graph(flare$vertices, flare$edges) %>%
      mutate(
        class = map_bfs_chr(node_is_root(), .f = function(node, dist, path, ...) {
          if (dist <= 1) {
            return(shortName[node])
          }
          path$result[[nrow(path)]]
        })
      )
    

    绘制矩形树状图

    ggraph(flareGraph, 'treemap', weight = size) +
      geom_node_tile(aes(fill = class, filter = leaf, alpha = depth), colour = NA) +
      geom_node_tile(aes(size = depth), colour = 'white') +
      scale_alpha(range = c(1, 0.5), guide = 'none') +
      scale_size(range = c(4, 0.2), guide = 'none')
    

    partition 布局中,可以绘制冰柱图

    ggraph(flareGraph, 'partition') +
      geom_node_tile(aes(y = -y, fill = class)) +
      theme_graph()
    

    在这里,我们设置了 y = -y 将冰柱图倒置

    1. geom_node_voronoi

    维诺图,将节点绘制成类似细胞一样的形状,根据节点将空间进行分割,可以避免节点之间的重叠

    ggraph(g, layout = 'stress') +
      geom_node_voronoi(aes(fill = type, colour = type), alpha = 0.3) +
      geom_node_point() + 
      geom_edge_link() + 
      theme_graph()
    

    可以设置 max.radius 参数的值,让节点形状看起来更像细胞

    ggraph(g, layout = 'stress') +
      geom_node_voronoi(aes(fill = type, colour = type), 
                        alpha = 0.3, max.radius = 1) +
      geom_node_point() + 
      geom_edge_link() + 
      theme_graph()
    
    1. geom_node_circle

    绘制圆形节点,必须与 coord_fixed() 搭配使用才能绘制圆形。

    ggraph(g, layout = 'stress') +
      geom_node_circle(aes(fill = type, 
                           colour = type, r = 0.2)) +
      geom_edge_link() + 
      coord_fixed() +
      theme_graph()
    

    必须在 aes 中设置 r 的值

    circlepack 布局搭配使用,可以绘制圆堆积图.

    ggraph(flareGraph, 'circlepack') +
      geom_node_circle(aes(fill = factor(depth))) +
      coord_fixed() +
      theme_graph()
    

    那为什么不用设置 r 的值呢?因为该布局返回值中包含了 r

    > head(create_layout(flareGraph, 'circlepack'))
              x          y          r circular  leaf depth          name size shortName   class .ggraph.orig_index
    1  0.000000  0.0000000 16.1255485    FALSE FALSE     0         flare    0     flare   flare                252
    2 -7.859692  9.2774060  3.6877643    FALSE FALSE     1 flare.animate    0   animate animate                224
    3 -4.679497  3.6143179  2.8071727    FALSE FALSE     1    flare.data    0      data    data                227
    4 -2.911247  7.5871414  1.5413945    FALSE FALSE     1 flare.display    0   display display                228
    5 -2.001902  1.5657652  0.5641896    FALSE FALSE     1    flare.flex    0      flex    flex                229
    6 -2.878393 -0.9334722  2.0842862    FALSE FALSE     1 flare.physics    0   physics physics                230
      .ggraph.index
    1             1
    2             2
    3             3
    4             4
    5             5
    6             6
    
    1. geom_node_arc_bar

    根据内外半径弧线绘制图形,主要与圆形 partition 布局搭配,绘制 sunburst

    ggraph(flareGraph, 'partition', circular = TRUE, weight = size) +
      geom_node_arc_bar(aes(fill = class)) +
      coord_fixed() +
      theme_graph()
    
    1. geom_node_range

    主要与 fabric 分布一起使用,将节点作为水平线

    ggraph(g, layout = 'fabric') +
      geom_node_range(aes(color = type, linetype = type), 
                      size = 0.8) +
      geom_edge_span() + 
      theme_graph()
    

    我们可以将节点作为数据点,那边就是 geom_segment() 了吗?可以这么理解,但是 ggraph 提供了更多的内容。

    直线只是边的其中一种表现形式,有时候甚至没有绘制,而是以一种容量或位置的形式表现,如 treemapcircle packingpartition 布局。但大多数时候都是以某种线条的形式来表现的。

    边的几何形状都是用 geom_edge_*() 函数来设置的,几乎每个几何形状都有三个不同水平的函数,即 geom_edge_*0()geom_edge_*2()

    其中,常规版本,即不带数字后缀的函数,会沿着边将其分割为一系列的点,并且每个点都有一个数字值 index,该值与点的位置相关。例如 colour = stat(index) 会沿着边的方向设置渐变颜色。

    2 后缀的函数为长边模式,在边的起始和终止节点插入节点参数,通常性能更低。只在需要的时候再用。0 后缀的函数是最高性能的版本,会忽略掉很多设置,追求极致的性能,没有 index 计算变量

    通常这些函数都有一些共同的参数用于对边进行设置:

    • edge_colour,也可以使用 colour 参数,会自动进行转换
    • edge_width
    • edge_linetype
    • edge_alpha
    • filter

    geom_edge_*()geom_edge_*2() 函数的参数还可以设置边的标签等:

    • start_cap
    • end_cap
    • label
    • label_pos
    • label_size
    • angle
    • hjust
    • vjust
    • family
    • fontface
    • lineheight

    1. link

    使用直线连接节点

    ggraph(g, layout = 'stress') + 
      geom_edge_link0(aes(colour = factor(corr))) +
      theme_graph()
    

    使用 index 变量来绘制渐变色

    ggraph(g, layout = 'stress') + 
      geom_edge_link(aes(colour = factor(corr), alpha = stat(index))) +
      theme_graph()
    

    使用节点的类型为边上色,记住不能直接使用 type,类型不同的节点之间的边会有两种颜色

    ggraph(g, layout = 'stress') + 
      geom_edge_link2(aes(colour = node.type)) +
      theme_graph()
    

    2. fan

    如果两个节点之间的边不只一条,那直接绘制直线的方式是不行的。对于存在平行边的图,可以使用 geom_edge_fan() 来绘制,不同的平行边,会绘制成不同曲率的圆弧,而没有平行边的还是绘制成直线。例如

    gr <- create_notable('bull') %>%
      convert(to_directed) %>%
      bind_edges(data.frame(from = c(1, 2, 2, 3), to = c(2, 1, 3, 2))) %E>%
      mutate(class = sample(letters[1:3], 9, TRUE)) %N>%
      mutate(class = sample(c('x', 'y'), 5, TRUE))
    
    ggraph(gr, 'stress') +
      geom_edge_fan2(aes(colour = node.class)) +
      theme_graph()
    

    其他设置与 link 相同

    上面的代码中 %E>% 管道表示对边数据框进行操作,%N>% 表示对节点数据框进行操作

    3. parallel

    fan 类似,将平行边绘制成平行线

    ggraph(gr, 'stress') +
      geom_edge_parallel0(aes(colour = class)) +
      theme_graph()
    

    4. loops

    如果图存在自循环,正常使用上面的方法是不会显示的,因为这种边是没有长度的,可以使用 geom_edge_loop

    ggraph(gr, 'stress') +
      geom_edge_loop(aes(colour = stat(index))) +
      geom_edge_fan(aes(colour = stat(index))) +
      theme_graph()
    

    自循环没有 geom_edge_loop2() 函数

    5. density

    添加密度阴影

    ggraph(g, layout = 'stress') + 
      geom_edge_density(aes(fill = factor(corr))) +
      geom_edge_link(alpha = 0.25) +
      theme_graph()
    

    6. arcs

    曲线边,通常与 linearcircular 布局一起使用。

    ggraph(g, 'linear') +
      geom_edge_arc2(aes(colour = node.type), strength = 0.6) +
      theme_graph()
    

    圆形布局

    ggraph(g, 'linear', circular = TRUE) +
      geom_edge_arc2(aes(colour = node.type), strength = 0.6) +
      theme_graph()
    

    7. elbow

    用于绘制树状图,用直角边连接两个节点

    irisDen <- hclust(dist(iris[1:4], method = 'euclidean'), method = 'ward.D2') %>%
      as_tbl_graph() %>%
      mutate(class = sample(letters[1:3], n(), TRUE)) %>%
      activate(edges) %>%
      mutate(class = sample(letters[1:3], n(), TRUE))
      
    ggraph(irisDen, 'dendrogram') +
      geom_edge_elbow2(aes(colour = node.class)) +
      theme_graph()
    

    上面的代码中,activate() 函数用于激活边或节点数据框,即 activate(edges) 表示后面的管道操作都是针对边数据框来进行的

    圆形布局

    ggraph(irisDen, 'dendrogram', circular = TRUE) +
      geom_edge_elbow(aes(colour = stat(index))) +
      coord_fixed() +
      theme_graph()
    

    8. diagonals

    将边绘制成对角贝塞尔曲线,也是一种树状图

    ggraph(irisDen, 'dendrogram') +
      geom_edge_diagonal0(aes(colour = class)) +
      theme_graph()
    

    9. bends

    也是绘制树状图,是 diagonals 的一种替代方案,相当于二次贝塞尔曲线,圆角连接线

    ggraph(irisDen, 'dendrogram') +
      geom_edge_bend2(aes(colour = node.class)) +
      theme_graph()
    

    10. hive

    该几何图形只能与 hive 布局一起使用,将边绘制为贝塞尔曲线

    g <- g %>%
      mutate(friends = ifelse(
        centrality_degree(mode = 'all') < 3, "few",
        ifelse(centrality_degree(mode = 'all') > 3, "many", "medium")
      ))
    
    ggraph(g, 'hive', axis = friends) + 
      geom_edge_hive(aes(colour = factor(corr))) + 
      geom_axis_hive(aes(colour = friends), size = 2, label = FALSE) + 
      coord_fixed() +
      theme_graph()
    

    11. span

    将边绘制为竖直线,用于连接水平线表示的节点,只能在 fabric 布局中使用

    ggraph(g, 'fabric', sort.by = node_rank_fabric()) + 
      geom_node_range(aes(colour = type)) + 
      geom_edge_span(aes(colour = factor(corr)), end_shape = 'circle') + 
      scale_edge_colour_brewer(palette = "Set1") +
      theme_graph()
    

    12. point and tile

    对于 matrix 布局,x 轴表示起始节点的位置,y 轴表示终止节点的位置,表可以表示为对应于 (x, y) 的图形

    对于下面的图

    gr <- create_notable('zachary') %>%
      mutate(group = group_infomap()) %>%
      morph(to_split, group) %>%
      activate(edges) %>%
      mutate(edge_group = as.character(.N()$group[1])) %>%
      unmorph()
    

    绘制不同形状的点来表示边

    ggraph(gr, 'matrix', sort.by = node_rank_hclust()) +
      geom_edge_point(aes(colour = edge_group, edge_shape = edge_group), 
                      mirror = TRUE, edge_size = 3) +
      scale_y_reverse() +
      coord_fixed() +
      theme_graph()
    

    或者使用矩形来表示

    ggraph(gr, 'matrix', sort.by = node_rank_hclust()) +
      geom_edge_tile(aes(fill = edge_group), 
                      mirror = TRUE, edge_size = 3) +
      scale_y_reverse() +
      coord_fixed() +
      theme_graph()
    

    边样式

    1. strength

    许多边几何图形都有一个 strength 参数,用于表示它们与直线的偏离程度,strength = 0 将会变成直线,默认样式对应于 strength = 1

    例如

    small_tree <- create_tree(10, 2)
    
    ggraph(small_tree, 'dendrogram') + 
      geom_edge_elbow(strength = 0.75) +
      theme_graph()
    
    ggraph(small_tree, 'dendrogram') + 
      geom_edge_diagonal(strength = 0.5) +
      theme_graph()
    

    2. 边的修饰

    边不仅仅只是一条线,它还可以添加标签和箭头

    2.1 箭头

    给边添加箭头的方式与 ggplot2 一样,例如

    ggraph(g, layout = 'graphopt') + 
      geom_edge_link(arrow = arrow(length = unit(4, 'mm'))) + 
      geom_node_point(size = 5) +
      theme_graph()
    

    但是这种不好看,箭头的顶点都延伸到节点的中心了,所以,我们需要设置边与两端节点之间的间隔

    ggraph(g, layout = 'graphopt') + 
      geom_edge_link(arrow = arrow(length = unit(4, 'mm')),
                     end_cap = circle(3, 'mm')) + 
      geom_node_point(size = 5) +
      theme_graph()
    

    可以看到,箭头已经与节点分开了。

    可以使用 circle()square()ellipsis()rectangle() 函数来设置不同类型的间隔

    ggraph(g, layout = 'stress') + 
      geom_edge_arc(aes(colour = corr), 
                    arrow = arrow(length = unit(4, 'mm')),
                    start_cap = square(3, 'mm'),
                    end_cap = circle(3, 'mm')) + 
      geom_node_point(aes(colour = type), size = 5) +
      theme_graph()
    

    对于纯文本的节点,可以计算标签的矩形范围来控制间隔

    ggraph(g, layout = 'graphopt') + 
      geom_edge_link(aes(colour = corr, 
                    start_cap = label_rect(node1.name),
                    end_cap = label_rect(node2.name)),
                    arrow = arrow(length = unit(4, 'mm'))) + 
      geom_node_text(aes(label = name), size = 3) +
      theme_graph()
    

    2.2 标签

    我们可以为边添加标签

    edges <- edges %>%
      mutate(corr = sample(-1:1, size = n(), replace = TRUE),
             type = ifelse(corr == -1, "Neg", 
                           ifelse(corr == 0, "None", "Pos"))
             )
    g <- tbl_graph(nodes = nodes, edges = edges)
    
    ggraph(g, layout = 'stress') + 
      geom_edge_link(aes(label = type, 
                    end_cap = circle(2, 'mm')),
                    arrow = arrow(length = unit(4, 'mm'))) + 
      geom_node_point(aes(colour = type), size = 3) +
      theme_graph()
    

    让标签沿着边放置

    ggraph(g, layout = 'stress') + 
      geom_edge_link(aes(label = type),
                     angle_calc = 'along',
                     label_dodge = unit(2.5, 'mm'),
                     end_cap = circle(2, 'mm'),
                     arrow = arrow(length = unit(4, 'mm'))) +
      geom_node_point(aes(colour = type), size = 3) +
      theme_graph()
    

    相关文章

      网友评论

        本文标题:R 数据可视化 —— ggraph 边与节点

        本文链接:https://www.haomeiwen.com/subject/rwwksltx.html