-
-
Notifications
You must be signed in to change notification settings - Fork 205
fix!: Subset assignment of a graph avoids addition of double edges and ignores loops unless the new loops argument is set to TRUE
#1661
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b7d284d
d9154a5
8b42e85
7fcfea9
f540d9f
2b0d7f4
5e04178
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,4 +1,3 @@ | ||||||
|
|
||||||
| ## IGraph library. | ||||||
| ## Copyright (C) 2010-2012 Gabor Csardi <csardi.gabor@gmail.com> | ||||||
| ## 334 Harvard street, Cambridge, MA 02139 USA | ||||||
|
|
@@ -329,14 +328,27 @@ length.igraph <- function(x) { | |||||
| vcount(x) | ||||||
| } | ||||||
|
|
||||||
| expand.grid.unordered <- function(i, j, loops = FALSE, directed = FALSE) { | ||||||
| grid <- vctrs::vec_expand_grid(i = i, j = j) | ||||||
| if (!directed) { | ||||||
| grid <- vctrs::vec_unique(data.frame( | ||||||
| i = pmin(grid$i, grid$j), | ||||||
| j = pmax(grid$i, grid$j) | ||||||
| )) | ||||||
| } | ||||||
| if (!loops) { | ||||||
| grid <- grid[grid[, 1] != grid[, 2], ] | ||||||
| } | ||||||
| grid | ||||||
| } | ||||||
|
|
||||||
| #' @method [<- igraph | ||||||
| #' @family functions for manipulating graph structure | ||||||
| #' @export | ||||||
| `[<-.igraph` <- function(x, i, j, ..., from, to, | ||||||
| attr = if (is_weighted(x)) "weight" else NULL, | ||||||
| loops = FALSE, | ||||||
| value) { | ||||||
| ## TODO: rewrite this in C to make it faster | ||||||
|
|
||||||
| ################################################################ | ||||||
| ## Argument checks | ||||||
| if ((!missing(from) || !missing(to)) && | ||||||
|
|
@@ -373,16 +385,16 @@ length.igraph <- function(x) { | |||||
| (is.logical(value) && !value) || | ||||||
| (is.null(attr) && is.numeric(value) && value == 0)) { | ||||||
| ## Delete edges | ||||||
| todel <- x[from = from, to = to, ..., edges = TRUE] | ||||||
| todel <- get_edge_ids(x, c(rbind(from, to))) | ||||||
| x <- delete_edges(x, todel) | ||||||
| } else { | ||||||
| ## Addition or update of an attribute (or both) | ||||||
| ids <- x[from = from, to = to, ..., edges = TRUE] | ||||||
| ids <- get_edge_ids(x, c(rbind(from, to))) | ||||||
| if (any(ids == 0)) { | ||||||
| x <- add_edges(x, rbind(from[ids == 0], to[ids == 0])) | ||||||
| } | ||||||
| if (!is.null(attr)) { | ||||||
| ids <- x[from = from, to = to, ..., edges = TRUE] | ||||||
| ids <- get_edge_ids(x, c(rbind(from, to))) | ||||||
| x <- set_edge_attr(x, attr, ids, value = value) | ||||||
| } | ||||||
| } | ||||||
|
|
@@ -391,37 +403,35 @@ length.igraph <- function(x) { | |||||
| (is.null(attr) && is.numeric(value) && value == 0)) { | ||||||
| ## Delete edges | ||||||
| if (missing(i) && missing(j)) { | ||||||
| todel <- unlist(x[[, , ..., edges = TRUE]]) | ||||||
| todel <- seq_len(ecount(x)) | ||||||
| } else if (missing(j)) { | ||||||
| todel <- unlist(x[[i, , ..., edges = TRUE]]) | ||||||
| todel <- unlist(incident_edges(x, v = i, mode = "out")) | ||||||
| } else if (missing(i)) { | ||||||
| todel <- unlist(x[[, j, ..., edges = TRUE]]) | ||||||
| todel <- unlist(incident_edges(x, v = j, mode = "in")) | ||||||
| } else { | ||||||
| todel <- unlist(x[[i, j, ..., edges = TRUE]]) | ||||||
| edge_pairs <- expand.grid(i, j) | ||||||
| edge_ids <- get_edge_ids(x, c(rbind(edge_pairs[, 1], edge_pairs[, 2]))) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this covered by tests?
Suggested change
The interface of
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Going through the existing tests, I realize there are some gaps. I will add a set of tests for this functionality
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It has been bothering me mildly for years as a user that edges need to be supplied as a vector (same with
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The c(rbind(1:3, 4:6))
#> [1] 1 4 2 5 3 6
c(t(data.frame(1:3, 4:6)))
#> [1] 1 4 2 5 3 6
as.vector(t(data.frame(1:3, 4:6)))
#> [1] 1 4 2 5 3 6Created on 2025-01-19 with reprex v2.1.1 There are two layers here: the C core and the R interface. We should provide an idiomatic R user interface that translates to what the C core needs.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
df <- as.data.frame(cbind(1:30, 4:33))
bench::mark(
c(t(df)),
c(rbind(df[, 1], df[, 2])),
c(rbind(df[[1]], df[[2]]))
)
#> # A tibble: 3 × 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 c(t(df)) 12.96µs 14.51µs 57926. 74.4KB 34.8
#> 2 c(rbind(df[, 1], df[, 2])) 5.33µs 5.99µs 157537. 576B 47.3
#> 3 c(rbind(df[[1]], df[[2]])) 3.65µs 4.26µs 219083. 576B 43.8
m <- cbind(1:30, 4:33)
bench::mark(
c(t(m)),
c(rbind(m[, 1], m[, 2]))
)
#> # A tibble: 2 × 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 c(t(m)) 820ns 983.94ns 964028. 576B 96.4
#> 2 c(rbind(m[, 1], m[, 2])) 984ns 1.15µs 791706. 576B 0Created on 2025-01-20 with reprex v2.1.1 Draft PR for new UI in #1663. |
||||||
| todel <- edge_ids[edge_ids != 0] | ||||||
| } | ||||||
| x <- delete_edges(x, todel) | ||||||
| } else { | ||||||
| ## Addition or update of an attribute (or both) | ||||||
| i <- if (missing(i)) as.numeric(V(x)) else as_igraph_vs(x, i) | ||||||
| j <- if (missing(j)) as.numeric(V(x)) else as_igraph_vs(x, j) | ||||||
| if (length(i) != 0 && length(j) != 0) { | ||||||
| ## Existing edges, and their endpoints | ||||||
| exe <- lapply(x[[i, j, ..., edges = TRUE]], as.vector) | ||||||
| exv <- lapply(x[[i, j, ...]], as.vector) | ||||||
| toadd <- unlist(lapply(seq_along(exv), function(idx) { | ||||||
| to <- setdiff(j, exv[[idx]]) | ||||||
| if (length(to != 0)) { | ||||||
| rbind(i[idx], setdiff(j, exv[[idx]])) | ||||||
| } else { | ||||||
| numeric() | ||||||
| } | ||||||
| })) | ||||||
| ## Do the changes | ||||||
| edge_pairs <- expand.grid.unordered(i, j, loops = loops, directed = is_directed(x)) | ||||||
|
|
||||||
| edge_ids <- get_edge_ids(x, c(rbind(edge_pairs[, 1], edge_pairs[, 2]))) | ||||||
|
schochastics marked this conversation as resolved.
|
||||||
| toadd <- c(rbind(edge_pairs[edge_ids == 0, 1], edge_pairs[edge_ids == 0, 2])) | ||||||
|
|
||||||
| if (is.null(attr)) { | ||||||
| if (value > 1) { | ||||||
| cli::cli_abort("value greater than one but graph is not weighted and no attribute was specified.") | ||||||
| } | ||||||
| x <- add_edges(x, toadd) | ||||||
| } else { | ||||||
| x <- add_edges(x, toadd, attr = structure(list(value), names = attr)) | ||||||
| toupdate <- unlist(exe) | ||||||
| toupdate <- edge_ids[edge_ids != 0] | ||||||
| x <- set_edge_attr(x, attr, toupdate, value) | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.