1#' Get detailed information on nodes
2#'
3#' Obtain a data frame with detailed information on nodes and their
4#' interrelationships within the graph.
5#'
6#' @param graph A graph object of class `dgr_graph`.
7#'
8#' @return A data frame containing information specific to each node within the
9#'   graph.
10#'
11#' @examples
12#' # Create a simple graph
13#' graph <-
14#'   create_graph() %>%
15#'   add_gnm_graph(
16#'     n = 5, m = 10,
17#'     set_seed = 23)
18#'
19#' # Get information on the graph's nodes
20#' graph %>% get_node_info()
21#'
22#' @export
23get_node_info <- function(graph) {
24
25  # Get the name of the function
26  fcn_name <- get_calling_fcn()
27
28  # Validation: Graph object is valid
29  if (graph_object_valid(graph) == FALSE) {
30
31    emit_error(
32      fcn_name = fcn_name,
33      reasons = "The graph object is not valid")
34  }
35
36  # If graph is empty, return NULL
37  if (is_graph_empty(graph)) {
38    return(NULL)
39  }
40
41  # Get vectors of nodes in edges and
42  # node `type` values
43  edge_from <- graph$edges_df$from
44  edge_to <- graph$edges_df$to
45  type <- graph$nodes_df$type
46
47  # Get vector of all node IDs and all labels
48  all_nodes <- graph$nodes_df$id
49  labels <- graph$nodes_df$label
50
51  # For graphs with no edges, create a
52  # `node_properties` data frame that doesn't
53  # need to consider any edge information
54  if (nrow(graph$edges_df) == 0) {
55
56    node_properties <-
57      as.data.frame(
58        mat.or.vec(
59          nr = length(all_nodes),
60          nc = 7),
61        stringsAsFactors = FALSE)
62
63    colnames(node_properties) <-
64      c("id", "type", "label", "deg",
65        "indeg", "outdeg", "loops")
66
67    node_properties[, 1] <- graph$nodes_df[, 1]
68    node_properties[, 2] <- graph$nodes_df[, 2]
69    node_properties[, 3] <- graph$nodes_df[, 3]
70
71    # Ensure that the `id` column is an integer
72    node_properties <-
73      dplyr::mutate(node_properties, id = as.integer(id))
74
75    # Arrange the table by `id` ascending
76    node_properties <-
77      dplyr::arrange(node_properties, id)
78
79  } else if (!is.null(graph$edges_df)) {
80
81    # Get vector of the top-level nodes
82    top_nodes <-
83      unique(
84        edge_from[which(!(edge_from %in%
85                            edge_to))])
86
87    # Get vector of the bottom-level nodes
88    bottom_nodes <-
89      unique(
90        edge_to[which(!(edge_to %in%
91                          edge_from))])
92
93    # Get vector of all nodes neither at the top nor
94    # the bottom level
95    between_nodes <-
96      all_nodes[which(!(all_nodes %in%
97                          c(top_nodes,
98                            bottom_nodes)))]
99
100    # Place the nodes in order
101    ordered_nodes <-
102      c(top_nodes, between_nodes, bottom_nodes)
103
104    # Create data frame of node properties
105    for (i in seq(ordered_nodes)) {
106      if (i == 1) {
107        node_properties <-
108          as.data.frame(
109            mat.or.vec(nr = 0,
110                       nc = 7),
111            stringsAsFactors = FALSE)
112
113        colnames(node_properties) <-
114          c("id", "type", "label", "deg",
115            "indeg", "outdeg", "loops")
116      }
117
118      # Get degree for each node
119      degree <-
120        sum(c(graph$edges_df$from,
121              graph$edges_df$to) %in%
122              ordered_nodes[i])
123
124      # Get indegree for each node
125      if (ordered_nodes[i] %in%
126          top_nodes | degree == 0) {
127        indegree <- 0
128      }
129
130      if (!(ordered_nodes[i] %in%
131            top_nodes) & degree != 0) {
132        for (j in 1:sum(edge_to %in%
133                        ordered_nodes[i])) {
134          if (j == 1) {
135            indegree <- vector(mode = "character")
136          }
137          indegree <-
138            c(indegree,
139              edge_from[which(edge_to %in%
140                                ordered_nodes[i])[j]])
141        }
142        indegree <- length(indegree)
143      }
144
145      # Get outdegree for each node
146      if (ordered_nodes[i] %in%
147          bottom_nodes | degree == 0) {
148        outdegree <- 0
149      }
150
151      if (!(ordered_nodes[i] %in% bottom_nodes) &
152          degree != 0) {
153        for (j in 1:sum(edge_from %in%
154                        ordered_nodes[i])) {
155          if (j == 1) {
156            outdegree <- vector(mode = "character")
157          }
158          outdegree <-
159            c(outdegree,
160              edge_from[which(edge_from %in%
161                                ordered_nodes[i])[j]])
162        }
163        outdegree <- length(outdegree)
164      }
165
166      # Get number of loops for each node
167      loops <-
168        sum(graph$edges_df$from == graph$edges_df$to &
169              graph$edges_df$to == ordered_nodes[i])
170
171      # Collect information into `node_properties`
172      node_properties[i, 1] <-
173        ordered_nodes[i]
174
175      node_properties[i, 2] <-
176        ifelse(exists("type"),
177               type[which(all_nodes %in%
178                            ordered_nodes[i])],
179               rep(NA, length(ordered_nodes)))
180
181      node_properties[i, 3] <-
182        ifelse(!is.null(
183          labels[which(all_nodes %in%
184                         ordered_nodes[i])]),
185          labels[which(all_nodes %in%
186                         ordered_nodes[i])],
187          NA)
188
189      node_properties[i, 4] <- degree
190      node_properties[i, 5] <- indegree
191      node_properties[i, 6] <- outdegree
192      node_properties[i, 7] <- loops
193    }
194
195    # Ensure that the `id` column is an integer
196    node_properties <-
197      dplyr::mutate(
198        node_properties, id = as.integer(id))
199
200    # Arrange the table by `id` ascending
201    node_properties <-
202      dplyr::arrange(
203        node_properties, id)
204  }
205
206  node_properties
207}
208