Skip to content

Commit 4a8efce

Browse files
Copilotkrivit
andcommitted
Implement network methods for networkLite
Co-authored-by: krivit <15682462+krivit@users.noreply.github.com>
1 parent b5bb8d1 commit 4a8efce

3 files changed

Lines changed: 526 additions & 0 deletions

File tree

NAMESPACE

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,23 @@ S3method(delete.edges,networkLite)
2424
S3method(delete.network.attribute,networkLite)
2525
S3method(delete.vertex.attribute,networkLite)
2626
S3method(delete.vertices,networkLite)
27+
S3method(get.dyads.eids,networkLite)
2728
S3method(get.edge.attribute,networkLite)
2829
S3method(get.edge.value,networkLite)
30+
S3method(get.edgeIDs,networkLite)
31+
S3method(get.edges,networkLite)
2932
S3method(get.inducedSubgraph,networkLite)
33+
S3method(get.neighborhood,networkLite)
3034
S3method(get.network.attribute,networkLite)
3135
S3method(get.vertex.attribute,networkLite)
36+
S3method(has.edges,networkLite)
37+
S3method(is.adjacent,networkLite)
3238
S3method(is.na,networkLite)
3339
S3method(list.edge.attributes,networkLite)
3440
S3method(list.network.attributes,networkLite)
3541
S3method(list.vertex.attributes,networkLite)
3642
S3method(mixingmatrix,networkLite)
43+
S3method(network.density,networkLite)
3744
S3method(network.edgecount,networkLite)
3845
S3method(network.naedgecount,networkLite)
3946
S3method(networkLite,edgelist)

R/network_methods.R

Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
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

Comments
 (0)