@@ -44,48 +44,75 @@ star_graph <- function(...) {
4444# ' Primarily intended for cleaning up the result of an [`igraph::union()`],
4545# ' which adds duplicated attributes when attributes of the same name exist in
4646# ' multiple graphs. Searches for suffixes and consolidates attributes,
47- # ' taking the attribute from the first non-NA value observed.
47+ # ' taking the attribute from the first non-NA value observed. The function
48+ # ' rebuilds the graph from scratch as accessing attributes once, operating on
49+ # ' lists and then assigning them to a new graph is significantly faster than
50+ # ' manipulating attribiutes of the existing graph.
4851# '
4952# ' @param g task_graph object
5053# '
5154# ' @keywords internal
5255graph_dedup_attrs <- function (g ) {
53- # pattern appended to duplicated attributes
5456 re <- " _\\ d+$"
57+ v_all <- igraph :: vertex_attr(g )
58+ e_all <- igraph :: edge_attr(g )
59+ v_attr_names <- names(v_all )
60+ e_attr_names <- names(e_all )
61+ v_dup <- grep(re , v_attr_names , value = TRUE )
62+ e_dup <- grep(re , e_attr_names , value = TRUE )
5563
56- # de-duplicate vertex attributes
57- v_attrs <- igraph :: vertex_attr_names(g )
58- v_dup_attrs <- grep(re , v_attrs , value = TRUE )
59- v_dup_group <- sub(re , " " , v_dup_attrs )
60- v_dup_attrs <- split(v_dup_attrs , v_dup_group )
61- for (i in seq_along(v_dup_attrs )) {
62- attr_name <- names(v_dup_attrs [i ])
63- attr_value <- igraph :: vertex_attr(g , v_dup_attrs [[i ]][[1L ]])
64- g <- igraph :: delete_vertex_attr(g , v_dup_attrs [[i ]][[1L ]])
65- for (attr_dup_name in v_dup_attrs [[i ]][- 1L ]) {
66- is_na <- is.na(attr_value )
67- attr_value [is_na ] <- igraph :: vertex_attr(g , attr_dup_name )[is_na ]
68- g <- igraph :: delete_vertex_attr(g , attr_dup_name )
64+ # Nothing to deduplicate
65+ if (length(v_dup ) == 0 && length(e_dup ) == 0 ) return (g )
66+
67+ first_non_na <- function (list ) {
68+ out <- list [[1 ]]
69+ if (! any(is.na(out ))) return (out )
70+
71+ for (i in seq(2 , length(list ))) {
72+ is_na <- is.na(out )
73+ if (any(is_na )) out [is_na ] <- list [[i ]][is_na ]
6974 }
70- g <- igraph :: set_vertex_attr( g , attr_name , value = attr_value )
75+ out
7176 }
7277
73- # de-duplicate edge attributes
74- e_attrs <- igraph :: edge_attr_names(g )
75- e_dup_attrs <- grep(re , e_attrs , value = TRUE )
76- e_dup_group <- sub(re , " " , e_dup_attrs )
77- e_dup_attrs <- split(e_dup_attrs , e_dup_group )
78- for (i in seq_along(e_dup_attrs )) {
79- attr_name <- names(e_dup_attrs [i ])
80- attr_value <- igraph :: edge_attr(g , e_dup_attrs [[i ]][[1L ]])
81- g <- igraph :: delete_edge_attr(g , e_dup_attrs [[i ]][[1L ]])
82- for (attr_dup_name in e_dup_attrs [[i ]][- 1L ]) {
83- is_na <- is.na(attr_value )
84- attr_value [is_na ] <- igraph :: edge_attr(g , attr_dup_name )[is_na ]
85- g <- igraph :: delete_edge_attr(g , attr_dup_name )
86- }
87- g <- igraph :: set_edge_attr(g , attr_name , value = attr_value )
78+ groups <- split(v_dup , sub(re , " " , v_dup ))
79+ for (base in names(groups )) {
80+ cols <- groups [[base ]]
81+ v_all [[base ]] <- first_non_na(v_all [cols ])
82+ }
83+ v_all [v_dup ] <- NULL
84+
85+ groups <- split(e_dup , sub(re , " " , e_dup ))
86+ for (base in names(groups )) {
87+ cols <- groups [[base ]]
88+ e_all [[base ]] <- first_non_na(e_all [cols ])
89+ }
90+ e_all [e_dup ] <- NULL
91+
92+ vertices <- data.frame (name = V(g )$ name )
93+
94+ for (attr in setdiff(names(v_all ), " name" )) {
95+ vertices [[attr ]] <- v_all [[attr ]]
8896 }
8997
90- g
98+ # Build edges data.frame (keeps current edge order)
99+ el <- igraph :: as_edgelist(g , names = TRUE )
100+ edges <- data.frame (
101+ from = el [, 1 ],
102+ to = el [, 2 ]
103+ )
104+
105+ for (attr in names(e_all )) {
106+ edges [[attr ]] <- e_all [[attr ]]
107+ }
108+
109+ # Rebuild graph
110+ g_rebuilt <- igraph :: graph_from_data_frame(
111+ d = edges ,
112+ directed = TRUE ,
113+ vertices = vertices
114+ )
115+
116+ class(g_rebuilt ) <- class(g )
117+ g_rebuilt
91118}
0 commit comments