|
| 1 | + |
| 2 | +#' @rdname get.edgeIDs |
| 3 | +#' |
| 4 | +#' @title Get Edge IDs for Specified Dyads |
| 5 | +#' |
| 6 | +#' @param x A `networkLite` object. |
| 7 | +#' @param v Vertex ID. |
| 8 | +#' @param alter Vertex ID for the alter (optional). If NULL, returns incident edges. |
| 9 | +#' @param neighborhood Specifies which edges to return when alter is NULL. |
| 10 | +#' @param na.omit Logical; whether to exclude missing edges from the result. |
| 11 | +#' @param ... additional arguments. |
| 12 | +#' |
| 13 | +#' @return The edge ID (row index in `x$el`) for the specified dyad, or |
| 14 | +#' numeric(0) if the edge is not present. For directed networks, the edge |
| 15 | +#' from `v` to `alter` is returned. For undirected networks, the edge |
| 16 | +#' between `v` and `alter` is returned (order does not matter). |
| 17 | +#' |
| 18 | +#' @details |
| 19 | +#' Returns the edge ID for a single dyad. In networkLite, edge IDs are |
| 20 | +#' simply row indices within `x$el`. If the edge is not present, returns |
| 21 | +#' numeric(0). If `na.omit = TRUE`, missing edges (those with the "na" |
| 22 | +#' attribute set to TRUE) are excluded. |
| 23 | +#' |
| 24 | +#' @export |
| 25 | +#' |
| 26 | +get.edgeIDs.networkLite <- function(x, v, alter = NULL, |
| 27 | + neighborhood = c("out", "in", "combined"), |
| 28 | + na.omit = TRUE, ...) { |
| 29 | + neighborhood <- match.arg(neighborhood) |
| 30 | + |
| 31 | + if (is.null(alter)) { |
| 32 | + # Return incident edges when alter is NULL |
| 33 | + return(get.edges(x, v, neighborhood = neighborhood, na.omit = na.omit)) |
| 34 | + } |
| 35 | + |
| 36 | + v <- as.integer(v) |
| 37 | + alter <- as.integer(alter) |
| 38 | + |
| 39 | + if (length(v) != 1 || length(alter) != 1) { |
| 40 | + stop("get.edgeIDs requires scalar v and alter; use get.dyads.eids for vectors") |
| 41 | + } |
| 42 | + |
| 43 | + if (is.na(v) || is.na(alter) || v < 1 || v > network.size(x) || |
| 44 | + alter < 1 || alter > network.size(x)) { |
| 45 | + return(numeric(0)) |
| 46 | + } |
| 47 | + |
| 48 | + # For undirected networks, normalize the dyad so tail < head |
| 49 | + if (!is.directed(x) && v > alter) { |
| 50 | + temp <- v |
| 51 | + v <- alter |
| 52 | + alter <- temp |
| 53 | + } |
| 54 | + |
| 55 | + # Find the edge in the edgelist |
| 56 | + eid <- which(x$el$.tail == v & x$el$.head == alter) |
| 57 | + |
| 58 | + # If na.omit is TRUE, exclude edges with na = TRUE |
| 59 | + if (na.omit && length(eid) > 0 && isTRUE(x$el$na[eid])) { |
| 60 | + return(numeric(0)) |
| 61 | + } |
| 62 | + |
| 63 | + return(eid) |
| 64 | +} |
| 65 | + |
| 66 | + |
| 67 | +#' @rdname get.dyads.eids |
| 68 | +#' |
| 69 | +#' @title Get Edge IDs for Multiple Dyads |
| 70 | +#' |
| 71 | +#' @param x A `networkLite` object. |
| 72 | +#' @param tails Vector of tail vertex IDs. |
| 73 | +#' @param heads Vector of head vertex IDs (must be same length as tails). |
| 74 | +#' @param neighborhood Specifies which edges to consider. |
| 75 | +#' @param na.omit Logical; whether to exclude missing edges from the result. |
| 76 | +#' @param ... additional arguments. |
| 77 | +#' |
| 78 | +#' @return A list of edge IDs corresponding to the specified dyads. Each |
| 79 | +#' element is either a single edge ID or numeric(0) if the edge is not |
| 80 | +#' present. |
| 81 | +#' |
| 82 | +#' @details |
| 83 | +#' Vectorized version of `get.edgeIDs`. Returns a list where each element |
| 84 | +#' corresponds to the edge ID for the dyad (tails[i], heads[i]). |
| 85 | +#' |
| 86 | +#' @export |
| 87 | +#' |
| 88 | +get.dyads.eids.networkLite <- function(x, tails, heads, |
| 89 | + neighborhood = c("out", "in", "combined"), |
| 90 | + na.omit = TRUE, ...) { |
| 91 | + neighborhood <- match.arg(neighborhood) |
| 92 | + tails <- as.integer(tails) |
| 93 | + heads <- as.integer(heads) |
| 94 | + |
| 95 | + if (length(tails) != length(heads)) { |
| 96 | + stop("tails and heads must have the same length") |
| 97 | + } |
| 98 | + |
| 99 | + # Use lapply to get edge IDs for each dyad |
| 100 | + eids <- lapply(seq_along(tails), function(i) { |
| 101 | + get.edgeIDs(x, tails[i], heads[i], neighborhood = neighborhood, na.omit = na.omit) |
| 102 | + }) |
| 103 | + |
| 104 | + return(eids) |
| 105 | +} |
| 106 | + |
| 107 | + |
| 108 | +#' @rdname get.edges |
| 109 | +#' |
| 110 | +#' @title Get Edges |
| 111 | +#' |
| 112 | +#' @param x A `networkLite` object. |
| 113 | +#' @param v,alter Vertex IDs. If both are provided, returns edges between |
| 114 | +#' v and alter. If only v is provided, returns edges incident on v. |
| 115 | +#' @param neighborhood Specifies which edges to return: "out" for outgoing |
| 116 | +#' edges, "in" for incoming edges, "combined" for both. |
| 117 | +#' @param na.omit Logical; whether to exclude missing edges from the result. |
| 118 | +#' @param ... additional arguments. |
| 119 | +#' |
| 120 | +#' @return A vector of edge IDs. |
| 121 | +#' |
| 122 | +#' @details |
| 123 | +#' Returns edge IDs based on the vertex selection. If both `v` and `alter` |
| 124 | +#' are specified, returns edges between those vertices. If only `v` is |
| 125 | +#' specified, returns edges incident on `v` according to the `neighborhood` |
| 126 | +#' parameter. |
| 127 | +#' |
| 128 | +#' @export |
| 129 | +#' |
| 130 | +get.edges.networkLite <- function(x, v, alter, neighborhood = c("combined", "out", "in"), |
| 131 | + na.omit = TRUE, ...) { |
| 132 | + neighborhood <- match.arg(neighborhood) |
| 133 | + |
| 134 | + if (!missing(alter)) { |
| 135 | + # Get edges between v and alter |
| 136 | + return(unlist(get.dyads.eids(x, v, alter, na.omit = na.omit))) |
| 137 | + } |
| 138 | + |
| 139 | + v <- as.integer(v) |
| 140 | + |
| 141 | + # Get edges incident on v |
| 142 | + if (neighborhood == "out" || neighborhood == "combined") { |
| 143 | + out_edges <- which(x$el$.tail %in% v) |
| 144 | + } else { |
| 145 | + out_edges <- integer(0) |
| 146 | + } |
| 147 | + |
| 148 | + if (neighborhood == "in" || (neighborhood == "combined" && is.directed(x))) { |
| 149 | + in_edges <- which(x$el$.head %in% v) |
| 150 | + } else if (neighborhood == "combined" && !is.directed(x)) { |
| 151 | + # For undirected networks, also check heads |
| 152 | + in_edges <- which(x$el$.head %in% v) |
| 153 | + } else { |
| 154 | + in_edges <- integer(0) |
| 155 | + } |
| 156 | + |
| 157 | + eids <- unique(c(out_edges, in_edges)) |
| 158 | + |
| 159 | + # Filter out missing edges if na.omit is TRUE |
| 160 | + if (na.omit && length(eids) > 0) { |
| 161 | + eids <- eids[!NVL(x$el$na[eids], FALSE)] |
| 162 | + } |
| 163 | + |
| 164 | + return(eids) |
| 165 | +} |
| 166 | + |
| 167 | + |
| 168 | +#' @rdname get.neighborhood |
| 169 | +#' |
| 170 | +#' @title Get Neighborhood of Vertices |
| 171 | +#' |
| 172 | +#' @param x A `networkLite` object. |
| 173 | +#' @param v Vertex ID or vector of vertex IDs. |
| 174 | +#' @param type Specifies which neighbors to return: "out" for out-neighbors, |
| 175 | +#' "in" for in-neighbors, "combined" for both. |
| 176 | +#' @param na.omit Logical; whether to exclude neighbors connected by missing edges. |
| 177 | +#' @param ... additional arguments. |
| 178 | +#' |
| 179 | +#' @return A vector of vertex IDs representing the neighborhood of v. |
| 180 | +#' |
| 181 | +#' @details |
| 182 | +#' Returns the neighborhood (adjacent vertices) of the specified vertex or |
| 183 | +#' vertices. For directed networks, the type parameter controls whether |
| 184 | +#' out-neighbors, in-neighbors, or both are returned. |
| 185 | +#' |
| 186 | +#' @export |
| 187 | +#' |
| 188 | +get.neighborhood.networkLite <- function(x, v, type = c("combined", "out", "in"), |
| 189 | + na.omit = TRUE, ...) { |
| 190 | + type <- match.arg(type) |
| 191 | + v <- as.integer(v) |
| 192 | + |
| 193 | + neighbors <- integer(0) |
| 194 | + |
| 195 | + # Get out-neighbors (vertices that v points to) |
| 196 | + if (type == "out" || type == "combined") { |
| 197 | + out_idx <- which(x$el$.tail %in% v) |
| 198 | + if (na.omit) { |
| 199 | + out_idx <- out_idx[!NVL(x$el$na[out_idx], FALSE)] |
| 200 | + } |
| 201 | + neighbors <- c(neighbors, x$el$.head[out_idx]) |
| 202 | + } |
| 203 | + |
| 204 | + # Get in-neighbors (vertices that point to v) |
| 205 | + if (type == "in" || type == "combined") { |
| 206 | + in_idx <- which(x$el$.head %in% v) |
| 207 | + if (na.omit) { |
| 208 | + in_idx <- in_idx[!NVL(x$el$na[in_idx], FALSE)] |
| 209 | + } |
| 210 | + neighbors <- c(neighbors, x$el$.tail[in_idx]) |
| 211 | + } |
| 212 | + |
| 213 | + # Return unique neighbors, excluding v itself |
| 214 | + unique(setdiff(neighbors, v)) |
| 215 | +} |
| 216 | + |
| 217 | + |
| 218 | +#' @rdname is.adjacent |
| 219 | +#' |
| 220 | +#' @title Test for Edge Existence |
| 221 | +#' |
| 222 | +#' @param x A `networkLite` object. |
| 223 | +#' @param vi,vj Vertex IDs. |
| 224 | +#' @param na.omit Logical; whether to treat missing edges as non-existent. |
| 225 | +#' @param ... additional arguments. |
| 226 | +#' |
| 227 | +#' @return Logical indicating whether an edge exists from vi to vj (or |
| 228 | +#' between vi and vj for undirected networks). |
| 229 | +#' |
| 230 | +#' @details |
| 231 | +#' Tests whether an edge exists between the specified vertices. For directed |
| 232 | +#' networks, tests for an edge from vi to vj. For undirected networks, tests |
| 233 | +#' for an edge between vi and vj (order does not matter). |
| 234 | +#' |
| 235 | +#' @export |
| 236 | +#' |
| 237 | +is.adjacent.networkLite <- function(x, vi, vj, na.omit = FALSE, ...) { |
| 238 | + eid <- get.edgeIDs(x, vi, vj, na.omit = na.omit) |
| 239 | + length(eid) > 0 |
| 240 | +} |
| 241 | + |
| 242 | + |
| 243 | +#' @rdname network.density |
| 244 | +#' |
| 245 | +#' @title Calculate Network Density |
| 246 | +#' |
| 247 | +#' @param x A `networkLite` object. |
| 248 | +#' @param na.omit Logical; whether to exclude missing edges from the calculation. |
| 249 | +#' @param discount.bipartite Logical; for bipartite networks, whether to compute |
| 250 | +#' density based on within-mode edges (if FALSE) or only between-mode edges (if TRUE). |
| 251 | +#' @param ... additional arguments. |
| 252 | +#' |
| 253 | +#' @return The network density (proportion of possible edges that are present). |
| 254 | +#' |
| 255 | +#' @details |
| 256 | +#' Calculates the density of the network as the ratio of the number of edges |
| 257 | +#' to the number of possible edges. For directed networks, the number of |
| 258 | +#' possible edges is n*(n-1). For undirected networks, it is n*(n-1)/2, |
| 259 | +#' where n is the network size. For bipartite networks, the number of |
| 260 | +#' possible edges is n1*n2 when discount.bipartite = FALSE, where n1 and n2 |
| 261 | +#' are the sizes of the two modes. |
| 262 | +#' |
| 263 | +#' @export |
| 264 | +#' |
| 265 | +network.density.networkLite <- function(x, na.omit = TRUE, discount.bipartite = FALSE, ...) { |
| 266 | + n <- network.size(x) |
| 267 | + |
| 268 | + if (n == 0) { |
| 269 | + return(NaN) |
| 270 | + } |
| 271 | + |
| 272 | + edge_count <- network.edgecount(x, na.omit = na.omit) |
| 273 | + |
| 274 | + if (is.bipartite(x) && !discount.bipartite) { |
| 275 | + b1 <- x %n% "bipartite" |
| 276 | + b2 <- n - b1 |
| 277 | + max_edges <- b1 * b2 |
| 278 | + } else if (is.directed(x)) { |
| 279 | + max_edges <- n * (n - 1) |
| 280 | + } else { |
| 281 | + max_edges <- n * (n - 1) / 2 |
| 282 | + } |
| 283 | + |
| 284 | + if (max_edges == 0) { |
| 285 | + return(NaN) |
| 286 | + } |
| 287 | + |
| 288 | + edge_count / max_edges |
| 289 | +} |
| 290 | + |
| 291 | + |
| 292 | +#' @rdname has.edges |
| 293 | +#' |
| 294 | +#' @title Test for Edge Existence in Network |
| 295 | +#' |
| 296 | +#' @param net A `networkLite` object. |
| 297 | +#' @param v Vertex IDs to check for incident edges. Defaults to all vertices. |
| 298 | +#' @param ... additional arguments. |
| 299 | +#' |
| 300 | +#' @return Logical indicating whether the specified vertices have any incident edges. |
| 301 | +#' |
| 302 | +#' @details |
| 303 | +#' Returns TRUE if any of the specified vertices have at least one incident edge |
| 304 | +#' (excluding missing edges), FALSE otherwise. |
| 305 | +#' |
| 306 | +#' @export |
| 307 | +#' |
| 308 | +has.edges.networkLite <- function(net, v = seq_len(network.size(net)), ...) { |
| 309 | + if (length(v) == 0 || network.edgecount(net, na.omit = TRUE) == 0) { |
| 310 | + return(FALSE) |
| 311 | + } |
| 312 | + |
| 313 | + v <- as.integer(v) |
| 314 | + |
| 315 | + # Check if any edges involve the specified vertices |
| 316 | + any(net$el$.tail %in% v | net$el$.head %in% v) && |
| 317 | + any(!NVL(net$el$na[net$el$.tail %in% v | net$el$.head %in% v], FALSE)) |
| 318 | +} |
0 commit comments