Skip to content

Commit e388cfa

Browse files
committed
Accept numeric edge IDs
1 parent 9b06810 commit e388cfa

7 files changed

+83
-68
lines changed

R/data_frame.R

Lines changed: 63 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,17 @@ graph.data.frame <- function(d, directed = TRUE, vertices = NULL) { # nocov star
100100
#' is returned, in a list with named entries `vertices` and `edges`.
101101
#'
102102
#' @param d A data frame containing a symbolic edge list in the first two
103-
#' columns. Additional columns are considered as edge attributes. Since
103+
#' columns, as vertex names or vertex IDs.
104+
# Additional columns are considered as edge attributes. Since
104105
#' version 0.7 this argument is coerced to a data frame with
105-
#' `as.data.frame`.
106+
#' [as.data.frame()].
106107
#' @param directed Logical scalar, whether or not to create a directed graph.
107108
#' @param vertices A data frame with vertex metadata, or `NULL`. See
108109
#' details below. Since version 0.7 this argument is coerced to a data frame
109-
#' with `as.data.frame`, if not `NULL`.
110+
#' with [as.data.frame()], if not `NULL`.
110111
#' @return An igraph graph object for `graph_from_data_frame()`, and either a
111112
#' data frame or a list of two data frames named `edges` and
112-
#' `vertices` for `as.data.frame`.
113+
#' `vertices` for [as.data.frame()].
113114
#' @note For `graph_from_data_frame()` `NA` elements in the first two
114115
#' columns \sQuote{d} are replaced by the string \dQuote{NA} before creating
115116
#' the graph. This means that all `NA`s will correspond to a single
@@ -155,82 +156,90 @@ graph.data.frame <- function(d, directed = TRUE, vertices = NULL) { # nocov star
155156
#' @export
156157
graph_from_data_frame <- function(d, directed = TRUE, vertices = NULL) {
157158
d <- as.data.frame(d)
158-
if (!is.null(vertices)) {
159+
if (is.character(vertices) || is.factor(vertices)) {
160+
vertices <- data.frame(name = as.character(vertices))
161+
} else if (is.matrix(vertices)) {
159162
vertices <- as.data.frame(vertices)
163+
} else if (!is.null(vertices) && !is.data.frame(vertices)) {
164+
stop("`vertices` must be a data frame or a character vector if given")
160165
}
161166

162167
if (ncol(d) < 2) {
163-
stop("the data frame should contain at least two columns")
168+
stop("`d` should contain at least two columns")
164169
}
165170

166-
## Handle if some elements are 'NA'
167-
if (any(is.na(d[, 1:2]))) {
168-
warning("In `d' `NA' elements were replaced with string \"NA\"")
169-
d[, 1:2][is.na(d[, 1:2])] <- "NA"
170-
}
171-
if (!is.null(vertices) && any(is.na(vertices[, 1]))) {
172-
warning("In `vertices[,1]' `NA' elements were replaced with string \"NA\"")
173-
vertices[, 1][is.na(vertices[, 1])] <- "NA"
171+
if (!is.null(vertices) && ncol(vertices) >= 1) {
172+
names <- vertices$name
173+
if (!is.null(names)) {
174+
if (anyNA(names)) {
175+
warning('Replacing `NA` in vertex names in `vertices` with the string "NA"')
176+
names[is.na(names)] <- "NA"
177+
}
178+
if (anyDuplicated(names)) {
179+
stop("Duplicate vertex names")
180+
}
181+
}
182+
} else {
183+
names <- NULL
174184
}
175185

176-
names <- unique(c(as.character(d[, 1]), as.character(d[, 2])))
177-
if (!is.null(vertices)) {
178-
names2 <- names
179-
vertices <- as.data.frame(vertices)
180-
if (ncol(vertices) < 1) {
181-
stop("Vertex data frame contains no rows")
186+
if (is.numeric(d[[1]]) && !is.factor(d[[1]]) && is.numeric(d[[2]]) && !is.factor(d[[2]])) {
187+
edges <- rbind(d[[1]], d[[2]])
188+
names <- seq_len(max(edges, 0L))
189+
} else {
190+
if (is.null(names)) {
191+
names <- unique(c(as.character(d[[1]]), as.character(d[[2]])))
192+
}
193+
194+
if (is.null(vertices)) {
195+
vertices <- data.frame(name = names)
196+
} else if (!("name" %in% names(vertices))) {
197+
vertices <- cbind(data.frame(name = names), vertices)
182198
}
183-
names <- as.character(vertices[, 1])
184-
if (any(duplicated(names))) {
185-
stop("Duplicate vertex names")
199+
200+
name_edges <- rbind(as.character(d[[1]]), as.character(d[[2]]))
201+
202+
if (anyNA(name_edges)) {
203+
warning('Replacing `NA` in vertex names in `d` with the string "NA"')
204+
name_edges[is.na(name_edges)] <- "NA"
186205
}
187-
if (any(!names2 %in% names)) {
188-
stop("Some vertex names in edge list are not listed in vertex data frame")
206+
207+
edges <- matrix(match(name_edges, names), nrow = 2)
208+
if (anyNA(edges)) {
209+
stop(
210+
"Vertex name ",
211+
name_edges[is.na(edges)][[1]],
212+
" in edge list is not listed in vertex data frame"
213+
)
189214
}
190215
}
191216

192217
# create graph
193218
g <- make_empty_graph(n = 0, directed = directed)
194219

195220
# vertex attributes
196-
attrs <- list(name = names)
197-
if (!is.null(vertices)) {
198-
if (ncol(vertices) > 1) {
199-
for (i in 2:ncol(vertices)) {
200-
newval <- vertices[, i]
201-
if (inherits(newval, "factor")) {
202-
newval <- as.character(newval)
203-
}
204-
attrs[[names(vertices)[i]]] <- newval
205-
}
206-
}
207-
}
221+
vattrs <- lapply(vertices, unfactor)
208222

209223
# add vertices
210-
g <- add_vertices(g, length(names), attr = attrs)
211-
212-
# create edge list
213-
from <- as.character(d[, 1])
214-
to <- as.character(d[, 2])
215-
edges <- rbind(match(from, names), match(to, names))
224+
g <- add_vertices(g, length(names), attr = vattrs)
216225

217226
# edge attributes
218-
attrs <- list()
219-
if (ncol(d) > 2) {
220-
for (i in 3:ncol(d)) {
221-
newval <- d[, i]
222-
if (inherits(newval, "factor")) {
223-
newval <- as.character(newval)
224-
}
225-
attrs[[names(d)[i]]] <- newval
226-
}
227-
}
227+
eattrs <- lapply(d[-1:-2], unfactor)
228228

229229
# add the edges
230-
g <- add_edges(g, edges, attr = attrs)
230+
g <- add_edges(g, edges, attr = eattrs)
231+
231232
g
232233
}
233234

235+
unfactor <- function(x) {
236+
if (!inherits(x, "factor")) {
237+
return(x)
238+
}
239+
240+
as.character(x)
241+
}
242+
234243
#' @rdname graph_from_data_frame
235244
#' @param ... Passed to `graph_from_data_frame()`.
236245
#' @export

man/graph.data.frame.Rd

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/graph_from_data_frame.Rd

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-betweenness.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,5 @@ test_that("shortest paths are compared with tolerance when calculating betweenne
9494
g <- graph_from_data_frame(edges, directed = FALSE)
9595
result <- betweenness(g, weights = edges.dists)
9696

97-
expect_that(result[1:5], equals(c("1" = 0, "2" = 44, "3" = 71, "4" = 36.5, "6" = 44)))
97+
expect_that(result[c(1:4, 6)], equals(c(0, 44, 71, 36.5, 44)))
9898
})

tests/testthat/test-get.shortest.paths.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ test_that("shortest_paths works", {
1818
edges <- as.data.frame(edges)
1919
edges[[3]] <- as.numeric(as.character(edges[[3]]))
2020

21-
g <- graph_from_data_frame(as.data.frame(edges))
21+
g <- graph_from_data_frame(edges)
2222

2323
all1 <- all_shortest_paths(g, "s", "t", weights = NA)$vpaths
2424

tests/testthat/test-graph.data.frame.R

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ test_that("graph_from_data_frame() creates attributes for zero-row data frames (
4040
test_that("graph_from_data_frame works on matrices", {
4141
el <- cbind(1:5, 5:1, weight = 1:5)
4242
g <- graph_from_data_frame(el)
43-
g <- delete_vertex_attr(g, "name")
4443
el2 <- as_data_frame(g)
4544
expect_that(as.data.frame(el), is_equivalent_to(el2))
4645
})

tests/testthat/test-graph.maxflow.R

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
test_that("max_flow works", {
2-
E <- rbind(c(1, 3, 3), c(3, 4, 1), c(4, 2, 2), c(1, 5, 1), c(5, 6, 2), c(6, 2, 10))
2+
E <- rbind(
3+
c(1, 3, 3),
4+
c(3, 4, 1),
5+
c(4, 2, 2),
6+
c(1, 5, 1),
7+
c(5, 6, 2),
8+
c(6, 2, 10)
9+
)
310
colnames(E) <- c("from", "to", "capacity")
411
g1 <- graph_from_data_frame(as.data.frame(E))
5-
fl <- max_flow(g1, source = "1", target = "2")
12+
fl <- max_flow(g1, source = 1, target = 2)
613
expect_that(fl$value, equals(2))
714
expect_that(as.vector(fl$flow), equals(rep(1, 6)))
815
expect_that(sort(as.vector(fl$cut)), equals(c(2, 4)))
9-
expect_that(sort(as.vector(fl$partition1)), equals(1:2))
10-
expect_that(sort(as.vector(fl$partition2)), equals(3:6))
16+
expect_that(sort(as.vector(fl$partition1)), equals(c(1, 3)))
17+
expect_that(sort(as.vector(fl$partition2)), equals(c(2, 4, 5, 6)))
1118
})

0 commit comments

Comments
 (0)