diff --git a/R/conversion.R b/R/conversion.R index ec548d01291..a387c3da37c 100644 --- a/R/conversion.R +++ b/R/conversion.R @@ -1,4 +1,3 @@ - #' Convert igraph graphs to graphNEL objects from the graph package #' #' @description @@ -159,11 +158,6 @@ get.adjacency.dense <- function(graph, type = c("both", "upper", "lower"), ensure_igraph(graph) type <- igraph.match.arg(type) - type <- switch(type, - "upper" = 0, - "lower" = 1, - "both" = 2 - ) if (is.logical(loops)) { loops <- ifelse(loops, "once", "ignore") @@ -183,61 +177,23 @@ get.adjacency.dense <- function(graph, type = c("both", "upper", "lower"), if (is.null(attr)) { on.exit(.Call(R_igraph_finalizer)) + type <- switch(type, + "upper" = 0, + "lower" = 1, + "both" = 2 + ) res <- .Call( R_igraph_get_adjacency, graph, as.numeric(type), weights, loops ) } else { - attr <- as.character(attr) - if (!attr %in% edge_attr_names(graph)) { - stop("no such edge attribute") - } - exattr <- edge_attr(graph, attr) - if (is.logical(exattr)) { - res <- matrix(FALSE, nrow = vcount(graph), ncol = vcount(graph)) - } else if (is.numeric(exattr)) { - res <- matrix(0, nrow = vcount(graph), ncol = vcount(graph)) - } else { - stop( - "Matrices must be either numeric or logical, ", - "and the edge attribute is not" - ) - } - if (is_directed(graph)) { - for (i in seq(length.out = ecount(graph))) { - e <- ends(graph, i, names = FALSE) - res[e[1], e[2]] <- exattr[i] - } - } else { - if (type == 0) { - ## upper - for (i in seq(length.out = ecount(graph))) { - e <- ends(graph, i, names = FALSE) - res[min(e), max(e)] <- exattr[i] - } - } else if (type == 1) { - ## lower - for (i in seq(length.out = ecount(graph))) { - e <- ends(graph, i, names = FALSE) - res[max(e), min(e)] <- exattr[i] - } - } else if (type == 2) { - ## both - for (i in seq(length.out = ecount(graph))) { - e <- ends(graph, i, names = FALSE) - res[e[1], e[2]] <- exattr[i] - if (e[1] != e[2]) { - res[e[2], e[1]] <- exattr[i] - } - } - } - } + # faster than a specialized implementation + res <- as.matrix(get.adjacency.sparse(graph, type = type, attr = attr, names = names)) } if (names && "name" %in% vertex_attr_names(graph)) { colnames(res) <- rownames(res) <- V(graph)$name } - res } @@ -378,7 +334,6 @@ as_adjacency_matrix <- function(graph, type = c("both", "upper", "lower"), as_adj <- function(graph, type = c("both", "upper", "lower"), attr = NULL, edges = deprecated(), names = TRUE, sparse = igraph_opt("sparsematrices")) { - lifecycle::deprecate_soft("2.1.0", "as_adj()", "as_adjacency_matrix()") as_adjacency_matrix( @@ -887,18 +842,22 @@ get.incidence.dense <- function(graph, types, names, attr) { res <- matrix(0, n1, n2) recode <- numeric(vc) + # move from 1..n indexing to 1..n1 row indices for type == FALSE + # and 1..n2 col indices for type == TRUE + # recode holds the mapping [1..n] -> [1..n1,1..n2] recode[!types] <- seq_len(n1) recode[types] <- seq_len(n2) - for (i in seq(length.out = ecount(graph))) { - eo <- ends(graph, i, names = FALSE) - e <- recode[eo] - if (!types[eo[1]]) { - res[e[1], e[2]] <- edge_attr(graph, attr, i) - } else { - res[e[2], e[1]] <- edge_attr(graph, attr, i) - } - } + el <- as_edgelist(graph, names = FALSE) + idx <- types[el[, 1]] + el[] <- recode[el] + + # switch order of source/target such that nodes with + # type == FALSE are in el[ ,1] + el[idx, ] <- el[idx, 2:1] + # el[ ,1] only holds values 1..n1 and el[ ,2] values 1..n2 + # and we can populate the matrix + res[el] <- edge_attr(graph, attr) if (names && "name" %in% vertex_attr_names(graph)) { rownames(res) <- V(graph)$name[which(!types)] @@ -1004,7 +963,7 @@ get.incidence.sparse <- function(graph, types, names, attr) { #' as_biadjacency_matrix(g) #' as_biadjacency_matrix <- function(graph, types = NULL, attr = NULL, - names = TRUE, sparse = FALSE) { + names = TRUE, sparse = FALSE) { # Argument checks ensure_igraph(graph) types <- handle_vertex_type_arg(types, graph) @@ -1033,8 +992,8 @@ as_biadjacency_matrix <- function(graph, types = NULL, attr = NULL, #' this naming to avoid confusion with the edge-vertex incidence matrix. #' @export as_incidence_matrix <- function(...) { # nocov start - lifecycle::deprecate_soft("1.6.0", "as_incidence_matrix()", "as_biadjacency_matrix()") - as_biadjacency_matrix(...) + lifecycle::deprecate_soft("1.6.0", "as_incidence_matrix()", "as_biadjacency_matrix()") + as_biadjacency_matrix(...) } # nocov end #' @rdname graph_from_data_frame #' @param x An igraph object. diff --git a/R/incidence.R b/R/incidence.R index 0a76209c684..b2a0a4c1cb0 100644 --- a/R/incidence.R +++ b/R/incidence.R @@ -1,4 +1,3 @@ - #' Create graphs from a bipartite adjacency matrix #' #' @description @@ -45,12 +44,11 @@ graph.incidence.sparse <- function(incidence, directed, mode, multiple, el[, 2] <- el[, 2] + n1 if (!is.null(weighted)) { - - if (!directed || mode == 1) { + if (!directed || mode == "out") { ## nothing do to - } else if (mode == 2) { + } else if (mode == "in") { el[, 1:2] <- el[, c(2, 1)] - } else if (mode == 3) { + } else if (mode %in% c("all", "total")) { reversed_el <- el[, c(2, 1, 3)] names(reversed_el) <- names(el) el <- rbind(el, reversed_el) @@ -68,11 +66,11 @@ graph.incidence.sparse <- function(incidence, directed, mode, multiple, el[, 3] <- el[, 3] != 0 } - if (!directed || mode == 1) { + if (!directed || mode == "out") { ## nothing do to - } else if (mode == 2) { + } else if (mode == "in") { el[, 1:2] <- el[, c(2, 1)] - } else if (mode == 3) { + } else if (mode %in% c("all", "total")) { el <- rbind(el, el[, c(2, 1, 3)]) } @@ -86,53 +84,43 @@ graph.incidence.sparse <- function(incidence, directed, mode, multiple, graph.incidence.dense <- function(incidence, directed, mode, multiple, weighted) { if (!is.null(weighted)) { - n1 <- nrow(incidence) n2 <- ncol(incidence) - no.edges <- sum(incidence != 0) - if (directed && mode == 3) { - no.edges <- no.edges * 2 - } - edges <- numeric(2 * no.edges) - weight <- numeric(no.edges) - ptr <- 1 - for (i in seq_len(nrow(incidence))) { - for (j in seq_len(ncol(incidence))) { - if (incidence[i, j] != 0) { - if (!directed || mode == 1) { - edges[2 * ptr - 1] <- i - edges[2 * ptr] <- n1 + j - weight[ptr] <- incidence[i, j] - ptr <- ptr + 1 - } else if (mode == 2) { - edges[2 * ptr - 1] <- n1 + j - edges[2 * ptr] <- i - weight[ptr] <- incidence[i, j] - ptr <- ptr + 1 - } else if (mode == 3) { - edges[2 * ptr - 1] <- i - edges[2 * ptr] <- n1 + j - weight[ptr] <- incidence[i, j] - ptr <- ptr + 1 - edges[2 * ptr - 1] <- n1 + j - edges[2 * ptr] <- i - weight[ptr] <- incidence[i, j] - ptr <- ptr + 1 - } - } - } + + # create an edgelist from the nonzero entries of the + # incidence matrix + idx <- which(incidence != 0, arr.ind = TRUE) + # add the value of the matrix. So a row is [s,t,incidence[s,t]] + el <- cbind(idx, incidence[idx]) + + # move from separate row/col indexing to 1..n1+n2 indexing + el[, 2] <- el[, 2] + n1 + + if (!directed || mode == "out") { + ## nothing do to + } else if (mode == "in") { + el[, 1:2] <- el[, c(2, 1)] + } else if (mode %in% c("all", "total")) { + reversed_el <- el[, c(2, 1, 3)] + names(reversed_el) <- names(el) + el <- rbind(el, reversed_el) } + res <- make_empty_graph(n = n1 + n2, directed = directed) - weight <- list(weight) + weight <- list(el[, 3]) names(weight) <- weighted - res <- add_edges(res, edges, attr = weight) - res <- set_vertex_attr(res, "type", - value = c(rep(FALSE, n1), rep(TRUE, n2)) - ) + res <- add_edges(res, edges = t(as.matrix(el[, 1:2])), attr = weight) + res <- set_vertex_attr(res, "type", value = c(rep(FALSE, n1), rep(TRUE, n2))) } else { mode(incidence) <- "double" on.exit(.Call(R_igraph_finalizer)) ## Function call + mode <- switch(mode, + "out" = 1, + "in" = 2, + "all" = 3, + "total" = 3 + ) res <- .Call(R_igraph_biadjacency, incidence, directed, mode, multiple) res <- set_vertex_attr(res$graph, "type", value = res$types) } @@ -140,6 +128,7 @@ graph.incidence.dense <- function(incidence, directed, mode, multiple, res } + #' Create graphs from a bipartite adjacency matrix #' #' `graph_from_biadjacency_matrix()` creates a bipartite igraph graph from an incidence @@ -203,22 +192,17 @@ graph.incidence.dense <- function(incidence, directed, mode, multiple, #' @family biadjacency #' @export graph_from_biadjacency_matrix <- function(incidence, directed = FALSE, - mode = c("all", "out", "in", "total"), - multiple = FALSE, weighted = NULL, - add.names = NULL) { + mode = c("all", "out", "in", "total"), + multiple = FALSE, weighted = NULL, + add.names = NULL) { # Argument checks directed <- as.logical(directed) - mode <- switch(igraph.match.arg(mode), - "out" = 1, - "in" = 2, - "all" = 3, - "total" = 3 - ) + mode <- igraph.match.arg(mode) + multiple <- as.logical(multiple) if (!is.null(weighted)) { if (is.logical(weighted) && weighted) { - if (multiple) { cli::cli_abort(c( "{.arg multiple} and {.arg weighted} cannot be both {.code TRUE}.", @@ -290,8 +274,8 @@ graph_from_biadjacency_matrix <- function(incidence, directed = FALSE, #' this naming to avoid confusion with the edge-vertex incidence matrix. #' @export from_incidence_matrix <- function(...) { # nocov start - lifecycle::deprecate_soft("1.6.0", "graph_from_incidence_matrix()", "graph_from_biadjacency_matrix()") - graph_from_biadjacency_matrix(...) + lifecycle::deprecate_soft("1.6.0", "graph_from_incidence_matrix()", "graph_from_biadjacency_matrix()") + graph_from_biadjacency_matrix(...) } # nocov end #' From incidence matrix #' @@ -308,6 +292,6 @@ from_incidence_matrix <- function(...) { # nocov start #' this naming to avoid confusion with the edge-vertex incidence matrix. #' @export graph_from_incidence_matrix <- function(...) { # nocov start - lifecycle::deprecate_soft("1.6.0", "graph_from_incidence_matrix()", "graph_from_biadjacency_matrix()") - graph_from_biadjacency_matrix(...) + lifecycle::deprecate_soft("1.6.0", "graph_from_incidence_matrix()", "graph_from_biadjacency_matrix()") + graph_from_biadjacency_matrix(...) } # nocov end