Diff of /R/2-2.manipulate.R [000000] .. [13df9a]

Switch to unified view

a b/R/2-2.manipulate.R
1
# ==========2.1 manipulate========
2
3
#' Set basic attributes from totu table
4
#'
5
#' @param go metanet an igraph object
6
#' @param ... some data.frames to annotate go
7
#' @param vertex_group choose which column to be vertex_group (map to vertex_shape)
8
#' @param vertex_class choose which column to be vertex_class (map to vertex_color)
9
#' @param vertex_size choose which column to be vertex_size (map to vertex_size)
10
#' @param edge_type choose which column to be edge_type (map to edge_color)
11
#' @param edge_class choose which column to be edge_class (map to edge_linetype)
12
#' @param edge_width choose which column to be edge_width (map to edge_width)
13
#' @param node_break node_break if v_class is numeric, default: 5
14
#' @param edge_break edge_break if e_type is numeric, default: 5
15
#' @param initialize initialize, default: TRUE
16
#'
17
#' @return a metanet object
18
#' @export
19
#' @family build
20
#' @examples
21
#' data("otutab", package = "pcutils")
22
#' t(otutab) -> totu
23
#' metadata[, 3:10] -> env
24
#'
25
#' data("c_net")
26
#' co_net <- c_net_set(co_net, taxonomy, data.frame("Abundance" = colSums(totu)),
27
#'   vertex_class = "Phylum", vertex_size = "Abundance"
28
#' )
29
#' co_net2 <- c_net_set(co_net2, taxonomy, data.frame(name = colnames(env), env = colnames(env)),
30
#'   vertex_class = c("Phylum", "env")
31
#' )
32
#' co_net2 <- c_net_set(co_net2, data.frame("Abundance" = colSums(totu)), vertex_size = "Abundance")
33
c_net_set <- function(go, ..., vertex_group = "v_group", vertex_class = "v_class", vertex_size = "size",
34
                      edge_type = "e_type", edge_class = "e_class", edge_width = "width",
35
                      node_break = 5, edge_break = 5, initialize = TRUE) {
36
  size <- e_class <- width <- NULL
37
  c_net_update(go, verbose = FALSE) -> go
38
  name <- v_group <- v_class <- e_type <- color <- NULL
39
40
  # annotation vertex
41
  anno_dfs <- list(...)
42
  if (length(anno_dfs) > 0) {
43
    anno_dfs2 <- list()
44
    for (i in seq_len(length(anno_dfs))) {
45
      x <- anno_dfs[[i]]
46
      if ("name" %in% colnames(x)) {
47
        rownames(x) <- x$name
48
        x <- dplyr::select(x, -name)
49
      }
50
      anno_dfs2[[i]] <- x
51
    }
52
53
    if (any(duplicated(lapply(anno_dfs2, names) %>% unlist()))) stop("Duplicated column names in your annotation tables, please check!")
54
55
    Reduce(\(x, y)merge(x, y, by = "row.names", all = TRUE) %>%
56
      tibble::column_to_rownames("Row.names"), anno_dfs2) -> all_anno
57
58
    anno_vertex(go, all_anno) -> go
59
  }
60
  get_v(go) -> v_index
61
  get_e(go) -> e_index
62
63
  # set something
64
  # !!!这里的set要改成跟c_net_update一样的逻辑
65
  if (!setequal(vertex_group, "v_group")) dplyr::select(v_index, v_group, !!vertex_group) %>% condance() -> v_index$v_group
66
67
  if (!setequal(vertex_class, "v_class")) {
68
    old_color <- twocol2vector(v_index[, c("v_class", "color")])
69
    new_color_name <- c()
70
71
    # 给每一个v_group加上v_class调整颜色
72
    # 可能某一个group用numeric做v_class,所以要分开上色
73
    for (i in unique(v_index$v_group)) {
74
      tmp_index <- v_index[v_index$v_group == i, ]
75
      tmp_v_class <- dplyr::select(tmp_index, v_class, !!vertex_class) %>% condance()
76
      if (identical(tmp_v_class, tmp_index$v_class)) {
77
        new_color_name <- c(new_color_name, unique(tmp_index$v_class))
78
        next
79
      }
80
      if (is.numeric(tmp_v_class)) {
81
        tmp_v_color <- color_generate(tmp_v_class, n_break = node_break, mode = "v")
82
        tmp_v_class <- color_generate(tmp_v_class, n_break = node_break, mode = "label")
83
        v_index[v_index$v_group == i, "color"] <- tmp_v_color
84
      } else {
85
        new_color_name <- c(new_color_name, unique(tmp_index$v_class))
86
      }
87
      v_index[v_index$v_group == i, "v_class"] <- as.character(tmp_v_class)
88
    }
89
    # 总体分类颜色是否改变,没变的话就不该,变了的话全部重新赋
90
    new_color_name <- unique(new_color_name)
91
    if (!all(new_color_name %in% names(old_color))) {
92
      new_color <- setNames(pcutils::get_cols(length(new_color_name), pal = default_v_color), new_color_name)
93
      v_index$color <- condance(data.frame(
94
        v_index$color,
95
        pcutils::tidai(v_index$v_class, new_color)
96
      ))
97
    }
98
  }
99
100
  if (!setequal(vertex_size, "size")) dplyr::select(v_index, size, !!vertex_size) %>% condance() -> v_index$size
101
102
  if (!setequal(edge_type, "e_type")) {
103
    tmp_e_type <- dplyr::select(e_index, e_type, !!edge_type) %>% condance()
104
    if (!identical(tmp_e_type, e_index$e_type)) {
105
      tmp_e_color <- color_generate(tmp_e_type, edge_break, mode = "e")
106
      tmp_e_type <- color_generate(tmp_e_type, edge_break, mode = "label")
107
      e_index$e_type <- tmp_e_type
108
      e_index$color <- tmp_e_color
109
    }
110
  }
111
  if (!setequal(edge_class, "e_class")) dplyr::select(e_index, e_class, !!edge_class) %>% condance() -> e_index$e_class
112
  if (!setequal(edge_width, "width")) dplyr::select(e_index, width, !!edge_width) %>% condance() -> e_index$width
113
114
  as.list(v_index) -> igraph::vertex.attributes(go)
115
  as.list(e_index) -> igraph::edge.attributes(go)
116
117
  c_net_update(go, initialize = initialize, verbose = FALSE) -> go2
118
  return(go2)
119
}
120
121
122
#' Is this object a metanet object?
123
#'
124
#' @param go a test object
125
#'
126
#' @return logical
127
#' @export
128
#' @aliases is.metanet
129
#' @family manipulate
130
#' @examples
131
#' data(c_net)
132
#' is_metanet(co_net)
133
is_metanet <- function(go) {
134
  is.igraph(go) & inherits(go, "metanet")
135
}
136
137
#' Get vertex information
138
#'
139
#' @param go metanet object
140
#' @param index attribute name, default: NULL
141
#' @family manipulate
142
#' @return data.frame
143
#' @export
144
get_v <- function(go, index = NULL) {
145
  # 规定name只能为字符
146
  if (is.null(V(go)$name)) V(go)$name <- as.character(V(go))
147
  # df <- as.data.frame(igraph::vertex.attributes(go))
148
  igraph::as_data_frame(go, what = "vertices") -> df
149
  df <- dplyr::select(df, name, dplyr::everything())
150
  rownames(df) <- NULL
151
  if (!is.null(index)) {
152
    return(dplyr::select(df, !!index))
153
  } else {
154
    return(df)
155
  }
156
}
157
158
#' Get edge information
159
#' @param go metanet object
160
#' @param index attribute name, default: NULL
161
#' @return data.frame
162
#' @family manipulate
163
#' @export
164
get_e <- function(go, index = NULL) {
165
  id <- NULL
166
  tmp_e <- cbind_new(igraph::as_data_frame(go), data.frame(id = seq_len(igraph::ecount(go))))
167
  tmp_e <- dplyr::select(tmp_e, id, dplyr::everything())
168
  if (!is.null(index)) {
169
    return(dplyr::select(tmp_e, !!index))
170
  } else {
171
    return(tmp_e)
172
  }
173
}
174
175
#' Get network information
176
#'
177
#' @param go metanet object
178
#' @param index attribute name, default: NULL
179
#' @param simple logical, get simple index
180
#' @family manipulate
181
#' @return data.frame
182
#' @export
183
get_n <- function(go, index = NULL, simple = FALSE) {
184
  gls <- igraph::graph.attributes(go)
185
  if (simple) {
186
    gls <- lapply(gls, \(x){
187
      if (inherits(x, "data.frame")) {
188
        return(NULL)
189
      }
190
      if (is.array(x)) {
191
        return(NULL)
192
      }
193
      if (is.list(x)) {
194
        return(NULL)
195
      }
196
      if (length(x) > 1) {
197
        return(NULL)
198
      }
199
      return(x)
200
    })
201
  } else {
202
    gls <- lapply(gls, \(x){
203
      if (inherits(x, "data.frame")) {
204
        return(paste0(ncol(x), "-columns df"))
205
      }
206
      if (is.array(x)) {
207
        return(paste0(length(x), "-elements ", class(x)))
208
      }
209
      if (is.list(x)) {
210
        return(paste0(length(x), "-elements ", class(x)))
211
      }
212
      if (length(x) > 1) {
213
        return(paste0(length(x), "-elements vector"))
214
      }
215
      return(x)
216
    })
217
  }
218
  df <- as.data.frame(do.call(cbind, gls))
219
  if (!is.null(index)) {
220
    return(dplyr::select(df, !!index))
221
  } else {
222
    return(df)
223
  }
224
}
225
226
#' Filter a network according to some attributes
227
#'
228
#' @param go metanet object
229
#' @param ... some attributes of vertex and edge
230
#' @param mode "v" or "e"
231
#'
232
#' @return metanet
233
#' @export
234
#' @family manipulate
235
#' @examples
236
#' data("multi_net")
237
#' c_net_filter(multi1, v_group %in% c("omic1", "omic2"))
238
c_net_filter <- function(go, ..., mode = "v") {
239
  if (mode == "v") {
240
    go1 <- filter_v(go, ...)
241
  } else if (mode == "e") {
242
    go1 <- filter_e(go, ...)
243
  } else {
244
    stop("mode should be 'v' or 'e'")
245
  }
246
  if (length(V(go1)) == 0) {
247
    message("The network is empty.")
248
  }
249
  go1
250
}
251
252
filter_v <- function(go, ...) {
253
  get_v(go) -> tmp_v
254
  tmp_v <- dplyr::filter(tmp_v, ...)
255
  tmp_v$name -> vid
256
  igraph::subgraph(go, vid) -> go1
257
  class(go1) <- c("metanet", "igraph")
258
  go1
259
}
260
261
filter_e <- function(go, ...) {
262
  get_e(go) -> tmp_e
263
  tmp_e <- dplyr::filter(tmp_e, ...)
264
  tmp_e$id -> eid
265
  igraph::subgraph.edges(go, eid) -> go1
266
  class(go1) <- c("metanet", "igraph")
267
  go1
268
}
269
270
271
#' Union two networks
272
#'
273
#' @param go1 metanet object
274
#' @param go2 metanet object
275
#'
276
#' @return metanet
277
#' @export
278
#' @family manipulate
279
#' @examples
280
#' data("c_net")
281
#' co_net_union <- c_net_union(co_net, co_net2)
282
#' c_net_plot(co_net_union)
283
c_net_union <- function(go1, go2) {
284
  tmp_v1 <- get_v(go1)
285
  tmp_v2 <- get_v(go2)
286
  cols <- c("name", "label", "size", "v_group", "shape", "v_class", "color")
287
  tmp_v <- rbind(tmp_v1[cols], tmp_v2[cols])
288
  message("Duplicated vertexes: ", sum(duplicated(tmp_v$name)), "\nUse the attributes of the first network.")
289
  tmp_v <- tmp_v[!duplicated(tmp_v$name), ]
290
291
  tmp_e1 <- get_e(go1)
292
  tmp_e2 <- get_e(go2)
293
  cols <- c("from", "to", "e_type", "color", "e_class", "lty", "width")
294
  tmp_e <- rbind(tmp_e1[cols], tmp_e2[cols])
295
  message("Duplicated edges: ", sum(duplicated(tmp_e[, c("from", "to")])), "\nUse the attributes of the first network.")
296
  tmp_e <- tmp_e[!duplicated(tmp_e[, c("from", "to")]), ]
297
298
  go <- igraph::union(go1, go2)
299
  go <- clean_igraph(go, direct = FALSE)
300
  go <- c_net_annotate(go, tmp_v, mode = "v")
301
  go <- c_net_annotate(go, tmp_e, mode = "e")
302
  go <- c_net_annotate(go, list(n_type = "combine_net"), mode = "n")
303
  go <- c_net_update(go, initialize = TRUE)
304
  go
305
}
306
307
308
#' Annotate a metanet
309
#'
310
#' @param go metanet object
311
#' @param anno_tab a dataframe using to annotate (mode v, e), or a list (mode n)
312
#' @param mode "v" for vertex, "e" for edge, "n" for network
313
#' @param verbose logical
314
#'
315
#' @return a annotated metanet object
316
#' @export
317
#' @family manipulate
318
#' @examples
319
#' data("c_net")
320
#' anno <- data.frame("name" = "s__Pelomonas_puraquae", new_atr = "new")
321
#' co_net_new <- c_net_annotate(co_net, anno, mode = "v")
322
#' get_v(co_net_new, c("name", "new_atr"))
323
#'
324
#' anno <- data.frame("from" = "s__Pelomonas_puraquae", "to" = "s__un_g__Rhizobium", new_atr = "new")
325
#' co_net_new <- c_net_annotate(co_net, anno, mode = "e")
326
#' get_e(co_net_new, c("from", "to", "new_atr"))
327
#'
328
#' co_net_new <- c_net_annotate(co_net, list(new_atr = "new"), mode = "n")
329
#' get_n(co_net_new)
330
c_net_annotate <- function(go, anno_tab, mode = "v", verbose = TRUE) {
331
  mode <- match.arg(mode, c("v", "e", "n"))
332
  if (mode == "v") {
333
    anno_vertex(go, anno_tab, verbose = verbose) -> go
334
  } else if (mode == "e") {
335
    anno_edge(go, anno_tab, verbose = verbose) -> go
336
  } else if (mode == "n") {
337
    igraph::graph.attributes(go) <-
338
      pcutils::update_param(igraph::graph.attributes(go), anno_tab)
339
  }
340
  go
341
}
342
343
344
#' Use data.frame to annotate vertexes of metanet
345
#'
346
#' @param go metanet object
347
#' @param verbose logical
348
#' @param anno_tab a dataframe using to annotate (with rowname or a "name" column)
349
#'
350
#' @return a annotated metanet object
351
#' @aliases anno_node
352
#' @export
353
#' @family manipulate
354
#' @examples
355
#' data("c_net")
356
#' data("otutab", package = "pcutils")
357
#' anno_vertex(co_net, taxonomy)
358
anno_vertex <- function(go, anno_tab, verbose = TRUE) {
359
  if (is.null(anno_tab)) {
360
    return(go)
361
  }
362
  get_v(go) -> v_atr
363
  if (!"name" %in% colnames(anno_tab)) rownames(anno_tab) -> anno_tab$name
364
  if (any(duplicated(anno_tab$name))) {
365
    stop(
366
      "Duplicated name in annotation tables: ",
367
      paste0(anno_tab$name[duplicated(anno_tab$name)], collapse = ", ")
368
    )
369
  }
370
  v_atr <- dplyr::left_join(v_atr, anno_tab, by = "name", suffix = c(".x", ""))
371
  grep(".x", colnames(v_atr), value = TRUE) %>% gsub(".x", "", .) -> du
372
  if (length(du) > 0) message(length(du), (" attributes will be overwrited:\n"), paste0(du, collapse = ", "), "\n")
373
  v_atr %>% dplyr::select(!dplyr::ends_with(".x")) -> v_atr
374
375
  as.list(v_atr) -> igraph::vertex.attributes(go)
376
  return(go)
377
}
378
379
#' Use dataframe to annotate edges of an igraph
380
#'
381
#' @param go metanet an igraph object
382
#' @param verbose logical
383
#' @param anno_tab a dataframe using to annotate (with rowname or a name column)
384
#'
385
#' @return a annotated igraph object
386
#' @export
387
#' @family manipulate
388
#' @examples
389
#' data("c_net")
390
#' anno <- data.frame("from" = "s__Pelomonas_puraquae", "to" = "s__un_g__Rhizobium", new_atr = "new")
391
#' anno_edge(co_net, anno) -> anno_net
392
anno_edge <- function(go, anno_tab, verbose = TRUE) {
393
  name <- NULL
394
  if (is.null(anno_tab)) {
395
    return(go)
396
  }
397
  get_e(go) -> e_atr
398
  if (all(c("from", "to") %in% colnames(anno_tab))) {
399
    e_atr <- dplyr::left_join(e_atr, anno_tab, by = c("from", "to"), suffix = c(".x", ""))
400
    grep(".x", colnames(e_atr), value = TRUE) %>% gsub(".x", "", .) -> du
401
    if (length(du) > 0) {
402
      if (verbose) message(length(du), (" attributes will be overwrited:\n"), paste0(du, collapse = ","), "\n")
403
    }
404
    e_atr %>% dplyr::select(!dplyr::ends_with(".x")) -> e_atr
405
  } else {
406
    if (verbose) message("No 'from' and 'to' columns in annotation table, will use 'name_from' and 'name_to' instead.")
407
    if (!"name" %in% colnames(anno_tab)) rownames(anno_tab) -> anno_tab$name
408
    anno_tab %>% dplyr::select(name, dplyr::everything()) -> anno_tab
409
    # from
410
    tmp <- anno_tab
411
    colnames(tmp) <- paste0(colnames(anno_tab), "_from")
412
    e_atr <- dplyr::left_join(e_atr, tmp, by = c("from" = "name_from"), suffix = c(".x", ""))
413
    grep(".x", colnames(e_atr), value = TRUE) %>% gsub(".x", "", .) -> du
414
    if (length(du) > 0) {
415
      if (verbose) message(length(du), (" attributes will be overwrited:\n"), paste0(du, collapse = ","), "\n")
416
    }
417
    e_atr %>% dplyr::select(!dplyr::ends_with(".x")) -> e_atr
418
    # to
419
    tmp <- anno_tab
420
    colnames(tmp) <- paste0(colnames(anno_tab), "_to")
421
    e_atr <- dplyr::left_join(e_atr, tmp, by = c("to" = "name_to"), suffix = c(".x", ""))
422
    grep(".x", colnames(e_atr), value = TRUE) %>% gsub(".x", "", .) -> du
423
    if (length(du) > 0) {
424
      if (verbose) message(length(du), (" attributes will be overwrited:\n"), paste0(du, collapse = ","), "\n")
425
    }
426
    e_atr %>% dplyr::select(!dplyr::ends_with(".x")) -> e_atr
427
  }
428
  as.list(e_atr) -> igraph::edge.attributes(go)
429
  return(go)
430
}
431
432
#' Save network file
433
#'
434
#' @param go metanet network
435
#' @param filename filename
436
#' @param format "data.frame","graphml"
437
#' @return No value
438
#' @family manipulate
439
#' @export
440
c_net_save <- function(go, filename = "net", format = "data.frame") {
441
  if (format == "data.frame") {
442
    get_v(go) %>% write.csv(., paste0(filename, "_nodes.csv"), row.names = FALSE)
443
    get_e(go) %>%
444
      dplyr::select(-1) %>%
445
      write.csv(., paste0(filename, "_edges.csv"), row.names = FALSE)
446
  } else if (format == "graphml") {
447
    if ("id" %in% edge.attributes(go)) go <- igraph::delete_edge_attr(go, "id")
448
    if (!grepl("\\.graphml$", filename)) filename <- paste0(filename, ".graphml")
449
    igraph::write_graph(go, filename, format = "graphml")
450
  } else {
451
    if (!grepl(paste0("\\.", format), filename)) filename <- paste0(filename, ".", format)
452
    igraph::write_graph(go, filename, format = format)
453
  }
454
  message(paste0(filename, " saved sucessfully!"))
455
}
456
457
#' Load network file
458
#'
459
#' @inheritParams c_net_save
460
#'
461
#' @return metanet
462
#' @export
463
#' @family manipulate
464
c_net_load <- function(filename, format = "data.frame") {
465
  if (format == "data.frame") {
466
    nodes <- read.csv(paste0(filename, "_nodes.csv"), stringsAsFactors = FALSE)
467
    edges <- read.csv(paste0(filename, "_edges.csv"), stringsAsFactors = FALSE)
468
    c_net_from_edgelist(edges, vertex_df = nodes) -> go
469
  } else if (format == "cyjs") {
470
    lib_ps("jsonify", library = FALSE)
471
    if (!grepl("\\.cyjs$", filename)) filename <- paste0(filename, ".cyjs")
472
    jsonify::from_json(filename) -> G
473
474
    if (!is.data.frame(G$elements$nodes$data)) {
475
      names <- lapply(G$elements$nodes$data, names)
476
      comm_name <- Reduce(intersect, names)
477
      lapply(G$elements$nodes$data, \(i)i[comm_name]) -> G$elements$nodes$data
478
      G$elements$nodes$data <- list_to_dataframe(G$elements$nodes$data)
479
    }
480
481
    node <- cbind_new(G$elements$nodes$data, G$elements$nodes$position)
482
    node$y <- -node$y
483
    node <- node[, colnames(node) != "name"]
484
    colnames(node)[1] <- "name"
485
486
    edge <- G$elements$edges$data
487
    edge <- edge[, !colnames(edge) %in% c("from", "to")]
488
    colnames(edge)[1:3] <- c("id", "from", "to")
489
    c_net_from_edgelist(edge, node) -> go
490
  } else if (format == "graphml") {
491
    if (!grepl("\\.graphml$", filename)) filename <- paste0(filename, ".graphml")
492
    igraph::read_graph(filename, format = "graphml") -> go
493
    go <- c_net_update(go, initialize = TRUE)
494
  } else {
495
    if (!grepl(paste0("\\.", format), filename)) filename <- paste0(filename, ".", format)
496
    igraph::read_graph(filename, format = format) -> go
497
    go <- c_net_update(go, initialize = TRUE)
498
  }
499
  go
500
}
501
502
#' Summaries two columns information
503
#' @param df data.frame
504
#' @param from first column name or index
505
#' @param to second column name or index
506
#' @param count (optional) weight column, if no, each equal to 1
507
#' @param direct consider direct? default: FALSE
508
#'
509
#' @return data.frame
510
#' @export
511
#' @examples
512
#' test <- data.frame(
513
#'   a = sample(letters[1:4], 10, replace = TRUE),
514
#'   b = sample(letters[1:4], 10, replace = TRUE)
515
#' )
516
#' summ_2col(test, direct = TRUE)
517
#' summ_2col(test, direct = FALSE)
518
#' if (requireNamespace("circlize")) {
519
#'   summ_2col(test, direct = TRUE) %>% pcutils::my_circo()
520
#' }
521
summ_2col <- function(df, from = 1, to = 2, count = 3, direct = FALSE) {
522
  if (ncol(df) < 2) stop("need at least two columns")
523
  if (ncol(df) == 2) {
524
    tmp <- cbind(df, count = 1)
525
  } else {
526
    tmp <- dplyr::select(df, !!from, !!to, !!count)
527
  }
528
  cols <- colnames(tmp)
529
  colnames(tmp) <- c("from", "to", "count")
530
531
  if (direct) {
532
    tmp <- (dplyr::group_by(tmp, from, to) %>% dplyr::summarise(count = sum(count)))
533
    colnames(tmp) <- cols
534
    return(as.data.frame(tmp))
535
  }
536
537
  com <- \(group1, group2, levels){
538
    factor(c(group1, group2), levels = levels) %>% sort()
539
  }
540
541
  group <- factor(c(tmp[, 1], tmp[, 2]))
542
  tmp1 <- apply(tmp, 1, function(x) com(x[1], x[2], levels(group))) %>%
543
    t() %>%
544
    as.data.frame()
545
546
  tmp1 <- cbind(tmp1, tmp$count)
547
  colnames(tmp1) <- c("from", "to", "count")
548
  tmp1 <- dplyr::group_by(tmp1, from, to) %>% dplyr::summarise(count = sum(count))
549
  colnames(tmp1) <- cols
550
  return(as.data.frame(tmp1))
551
}
552
553
554
#' Get skeleton network according to a group
555
#'
556
#' @param go network
557
#' @param Group vertex column name
558
#' @param count take which column count, default: NULL
559
#' @param top_N top_N
560
#'
561
#' @return skeleton network
562
#' @export
563
#' @family topological
564
#' @examples
565
#' get_group_skeleton(co_net) -> ske_net
566
#' skeleton_plot(ske_net)
567
get_group_skeleton <- function(go, Group = "v_class", count = NULL, top_N = 8) {
568
  name <- v_group <- n <- NULL
569
  stopifnot(is_igraph(go))
570
  direct <- igraph::is_directed(go)
571
572
  if (!Group %in% vertex_attr_names(go)) stop("no Group named ", Group, " !")
573
  get_v(go) -> tmp_v
574
  tmp_v %>% dplyr::select(name, !!Group) -> nodeGroup
575
  colnames(nodeGroup) <- c("name", "Group")
576
  nodeGroup$Group <- as.factor(nodeGroup$Group)
577
  # summary edges counts in each e_type
578
  suppressMessages(anno_edge(go, nodeGroup) %>% get_e() -> edge)
579
  {
580
    if (is.null(count)) {
581
      edge$count <- 1
582
    } else {
583
      edge$count <- edge[, count]
584
    }
585
  }
586
  bb <- data.frame()
587
  for (i in unique(edge$e_type)) {
588
    tmp <- edge[edge$e_type == i, c("Group_from", "Group_to", "count")]
589
    tmp <- dplyr::mutate_if(tmp, is.factor, as.character)
590
    # tmp=pcutils:::gettop(tmp,top_N)
591
    bb <- rbind(bb, data.frame(summ_2col(tmp,
592
      direct = direct
593
    ), e_type = i))
594
  }
595
  tmp_go <- igraph::graph_from_data_frame(bb, directed = direct)
596
  nodeGroup <- cbind_new(nodeGroup, data.frame(v_group = tmp_v$v_group))
597
598
  # nodeGroup=mutate_all(nodeGroup,as.character)
599
  # nodeGroup=rbind(nodeGroup,c("others","others","others"))
600
601
  dplyr::distinct(nodeGroup, Group, v_group) %>% tibble::column_to_rownames("Group") -> v_group_tab
602
603
  V(tmp_go)$v_group <- v_group_tab[V(tmp_go)$name, "v_group"]
604
  V(tmp_go)$v_class <- V(tmp_go)$name
605
  V(tmp_go)$size <- stats::aggregate(tmp_v$size, by = list(tmp_v[, Group]), sum) %>%
606
    tibble::column_to_rownames("Group.1") %>%
607
    .[V(tmp_go)$name, "x"]
608
  suppressWarnings({
609
    V(tmp_go)$count <- tmp_v %>%
610
      dplyr::group_by_(Group) %>%
611
      dplyr::count() %>%
612
      tibble::column_to_rownames(Group) %>%
613
      .[V(tmp_go)$name, "n"]
614
  })
615
616
  tmp_go <- c_net_update(tmp_go, initialize = TRUE)
617
  get_e(tmp_go) -> tmp_e
618
619
  E(tmp_go)$width <- E(tmp_go)$label <- tmp_e$count
620
621
  graph.attributes(tmp_go)$n_type <- "skeleton"
622
  graph.attributes(tmp_go)$skeleton <- Group
623
  tmp_go
624
}
625
626
#' Skeleton plot
627
#'
628
#' @param ske_net skeleton
629
#' @param split_e_type split by e_type? default: TRUE
630
#' @param ... additional parameters for \code{\link[igraph]{igraph.plotting}}
631
#'
632
#' @export
633
#' @rdname get_group_skeleton
634
skeleton_plot <- function(ske_net, split_e_type = TRUE, ...) {
635
  e_type <- NULL
636
  params <- list(...)
637
  tmp_go <- ske_net
638
  if (get_n(tmp_go)$n_type != "skeleton") stop("Not a skeleton network")
639
  get_e(tmp_go) -> tmp_e
640
641
  if (split_e_type) {
642
    for (i in unique(tmp_e$e_type)) {
643
      # main plot
644
      tmp_go1 <- c_net_filter(tmp_go, e_type == i, mode = "e")
645
      do.call(c_net_plot, pcutils::update_param(
646
        list(go = tmp_go1, legend_number = TRUE, edge_width_range = c(1, 5)), params
647
      ))
648
    }
649
  } else {
650
    tmp_go <- clean_multi_edge_metanet(tmp_go)
651
    do.call(c_net_plot, pcutils::update_param(
652
      list(go = tmp_go, legend_number = TRUE, edge_width_range = c(1, 5)), params
653
    ))
654
  }
655
}
656
657
# 整理skeleton网络的边,使其尽量不重叠。
658
# 1.from-to都是自己时,添加edge.loop.angle
659
# 2.from-to一致时,添加edge.curved
660
# 3.from-to刚好相反时,添加edge.curved
661
662
663
#' Clean multi edge metanet to plot
664
#' @param go metanet object
665
#'
666
#' @return metanet object
667
#' @export
668
#'
669
#' @examples
670
#' g <- igraph::make_ring(2)
671
#' g <- igraph::add.edges(g, c(1, 1, 1, 1, 2, 1))
672
#' plot(g)
673
#' plot(clean_multi_edge_metanet(g))
674
clean_multi_edge_metanet <- function(go) {
675
  tmp_e <- get_e(go)
676
  tmp_e$loop.angle <- 0
677
  # tmp_e$curved=0
678
679
  summ_2col(tmp_e[, c("from", "to")], direct = FALSE) -> e_count
680
  filter(e_count, count > 1) -> multi_e_count
681
  for (i in seq_len(nrow(multi_e_count))) {
682
    from <- multi_e_count$from[i]
683
    to <- multi_e_count$to[i]
684
    count <- multi_e_count$count[i]
685
    if (from == to) {
686
      tmp_e[tmp_e$from == from & tmp_e$to == to, "loop.angle"] <- seq(0, 2 * pi, length = count + 1)[-(count + 1)]
687
    }
688
    # else {
689
    #   tmp_e[tmp_e$from%in%c(from,to) & tmp_e$to%in%c(from,to),"curved"] <- 0.2 # seq(0,1,length=count)
690
    # }
691
  }
692
693
  #   summ_2col(tmp_e[,c("from","to")],direct = TRUE) -> e_count
694
  #   filter(e_count,count>1) -> multi_e_count
695
  #   for (i in seq_len(nrow(multi_e_count))) {
696
  #     from=multi_e_count$from[i]
697
  #     to=multi_e_count$to[i]
698
  #     count=multi_e_count$count[i]
699
  #     if(from!=to){
700
  #       tmp_e[tmp_e$from==from & tmp_e$to==to,"curved"] <- seq(0.2,1,length=count)
701
  #     }
702
  #   }
703
704
  igraph::edge.attributes(go) <- as.list(tmp_e)
705
  go
706
}
707
708
#' Link summary of the network
709
#'
710
#' @param go igraph or metanet
711
#' @param group summary which group of vertex attribution in names(vertex_attr(go))
712
#' @param e_type "positive", "negative", "all"
713
#' @param topN topN of group, default: 10
714
#' @param mode 1~2
715
#' @param colors colors
716
#' @param plot_param plot parameters
717
#'
718
#' @return plot
719
#' @export
720
#' @family topological
721
#' @examples
722
#' if (requireNamespace("circlize")) {
723
#'   links_stat(co_net, topN = 10)
724
#'   module_detect(co_net) -> co_net_modu
725
#'   links_stat(co_net_modu, group = "module")
726
#' }
727
#' if (requireNamespace("corrplot")) {
728
#'   links_stat(co_net, topN = 10, mode = 2)
729
#' }
730
links_stat <- function(go, group = "v_class", e_type = "all",
731
                       topN = 10, colors = NULL, mode = 1, plot_param = list()) {
732
  color <- v_class <- shape <- left_leg_x <- from <- to <- n <- NULL
733
  direct <- is_directed(go)
734
  go <- c_net_set(go, vertex_class = group)
735
736
  get_v(go) -> v_index
737
  v_index %>% dplyr::select("name", "v_class") -> map
738
739
  suppressMessages(anno_edge(go, map) %>% get_e() -> edge)
740
  # statistics
741
  if (e_type != "all") edge %>% dplyr::filter(e_type == !!e_type) -> edge
742
  summ_2col(edge[, paste0("v_class", c("_from", "_to"))], direct = direct) -> bb
743
  colnames(bb) <- c("from", "to", "count")
744
745
  dplyr::group_by(bb, from) %>%
746
    dplyr::summarise(n = sum(count)) %>%
747
    dplyr::arrange(-n) %>%
748
    dplyr::top_n(topN, n) %>%
749
    dplyr::pull(from) -> nnn
750
751
  # plot
752
  bb2 <- mutate(bb,
753
    from = ifelse(from %in% nnn, from, "Others"),
754
    to = ifelse(to %in% nnn, to, "Others")
755
  ) %>% summ_2col(direct = direct)
756
757
  if (mode == 1) {
758
    do.call(pcutils::my_circo, pcutils::update_param(
759
      list(
760
        df = bb2,
761
        reorder = FALSE,
762
        pal = colors
763
      ), plot_param
764
    ))
765
  }
766
  if (mode == 2) {
767
    tab <- pcutils::df2distance(bb2)
768
    tab2 <- tab
769
    tab2[tab2 > 0] <- 1
770
    tab2[tab2 != 1] <- 0
771
    # tab2 <- trans(tab, "pa") %>% as.matrix()
772
    do.call(corrplot::corrplot, pcutils::update_param(
773
      list(
774
        corr = tab2,
775
        type = "lower",
776
        method = "color",
777
        col = c("white", "white", "red"),
778
        addgrid.col = "black",
779
        cl.pos = "n",
780
        tl.col = "black"
781
      ),
782
      plot_param
783
    ))
784
  }
785
}
786
787
# 每个分组可以构建一个网络,每个网络都可以用link_stat得到一些互作的数量(互作强度),可以再看这些数量和分组间某些指标的相关性。