diff --git a/NAMESPACE b/NAMESPACE index ca88b6b9..9eb54ad7 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -272,7 +272,6 @@ import(tidygraph) import(vctrs) importFrom(MASS,bandwidth.nrd) importFrom(MASS,kde2d) -importFrom(dplyr,"%>%") importFrom(dplyr,arrange) importFrom(dplyr,do) importFrom(dplyr,group_by) @@ -358,7 +357,6 @@ importFrom(igraph,delete_vertex_attr) importFrom(igraph,distances) importFrom(igraph,edge_attr) importFrom(igraph,ends) -importFrom(igraph,get.edge.ids) importFrom(igraph,gorder) importFrom(igraph,graph_attr) importFrom(igraph,gsize) diff --git a/R/autograph.R b/R/autograph.R index e16000a2..4c2cb53a 100644 --- a/R/autograph.R +++ b/R/autograph.R @@ -18,7 +18,7 @@ #' #' @examples #' library(tidygraph) -#' gr <- create_notable('herschel') %>% +#' gr <- create_notable('herschel') |> #' mutate(class = sample(letters[1:3], n(), TRUE)) %E>% #' mutate(weight = runif(n())) #' diff --git a/R/edges.R b/R/edges.R index 537851ce..926a0841 100644 --- a/R/edges.R +++ b/R/edges.R @@ -198,28 +198,28 @@ expand_edge_aes <- function(x) { } x } -#' @importFrom dplyr %>% group_by top_n ungroup +#' @importFrom dplyr group_by top_n ungroup collapse_all_edges <- function(edges) { from <- pmin(edges$from, edges$to) to <- pmax(edges$to, edges$from) id <- paste(from, to, sep = '-') if (anyDuplicated(id)) { edges$.id <- id - edges <- edges %>% - group_by(.data$.id) %>% - top_n(1) %>% + edges <- edges |> + group_by(.data$.id) |> + top_n(1) |> ungroup() } data_frame0(edges) } -#' @importFrom dplyr %>% group_by top_n ungroup +#' @importFrom dplyr group_by top_n ungroup collapse_dir_edges <- function(edges) { id <- paste(edges$from, edges$to, sep = '-') if (anyDuplicated(id)) { edges$.id <- id - edges <- edges %>% - group_by(.data$.id) %>% - top_n(1) %>% + edges <- edges |> + group_by(.data$.id) |> + top_n(1) |> ungroup() } data_frame0(edges) diff --git a/R/facet_graph.R b/R/facet_graph.R index fa882e27..6f830f52 100644 --- a/R/facet_graph.R +++ b/R/facet_graph.R @@ -21,7 +21,7 @@ #' #' @examples #' library(tidygraph) -#' gr <- as_tbl_graph(highschool) %>% +#' gr <- as_tbl_graph(highschool) |> #' mutate(popularity = as.character(cut(centrality_degree(mode = 'in'), #' breaks = 3, #' labels = c('low', 'medium', 'high') diff --git a/R/facet_nodes.R b/R/facet_nodes.R index 993abdb5..190d2fdb 100644 --- a/R/facet_nodes.R +++ b/R/facet_nodes.R @@ -10,7 +10,7 @@ #' #' @examples #' library(tidygraph) -#' gr <- as_tbl_graph(highschool) %>% +#' gr <- as_tbl_graph(highschool) |> #' mutate(popularity = as.character(cut(centrality_degree(mode = 'in'), #' breaks = 3, #' labels = c('low', 'medium', 'high') diff --git a/R/geom_axis_hive.R b/R/geom_axis_hive.R index fa50645e..cb23a274 100644 --- a/R/geom_axis_hive.R +++ b/R/geom_axis_hive.R @@ -1,12 +1,12 @@ #' @rdname ggraph-extensions #' @format NULL #' @usage NULL -#' @importFrom dplyr %>% group_by mutate slice ungroup +#' @importFrom dplyr group_by mutate slice ungroup #' @export StatAxisHive <- ggproto('StatAxisHive', StatFilter, setup_data = function(data, params) { - data <- data %>% - group_by(.data$angle, .data$section, .data$PANEL) %>% + data <- data |> + group_by(.data$angle, .data$section, .data$PANEL) |> mutate( x = min(.data$r) * cos(.data$angle[1]) * 1.1, y = min(.data$r) * sin(.data$angle[1]) * 1.1, @@ -14,8 +14,8 @@ StatAxisHive <- ggproto('StatAxisHive', StatFilter, yend = max(.data$r) * sin(.data$angle[1]) * 1.1, max_r = max(.data$r), min_r = min(.data$r) - ) %>% - slice(1) %>% + ) |> + slice(1) |> ungroup() data_frame0(data) }, @@ -26,7 +26,7 @@ StatAxisHive <- ggproto('StatAxisHive', StatFilter, #' @format NULL #' @usage NULL #' @importFrom grid textGrob nullGrob -#' @importFrom dplyr %>% group_by summarise +#' @importFrom dplyr group_by summarise #' @export GeomAxisHive <- ggproto('GeomAxisHive', GeomSegment, draw_panel = function(data, panel_scales, coord, label = TRUE, axis = TRUE, label_colour = 'black') { @@ -35,8 +35,8 @@ GeomAxisHive <- ggproto('GeomAxisHive', GeomSegment, data$xend <- data$xend / 1.1 data$yend <- data$yend / 1.1 data <- coord$transform(data, panel_scales) - label_data <- data %>% - group_by(.data$axis) %>% + label_data <- data |> + group_by(.data$axis) |> summarise( x = max(.data$max_r) * cos(mean(.data$angle)), y = max(.data$max_r) * sin(mean(.data$angle)), @@ -132,15 +132,15 @@ GeomAxisHive <- ggproto('GeomAxisHive', GeomSegment, #' @examples #' # Plot the flare import graph as a hive plot #' library(tidygraph) -#' flareGr <- as_tbl_graph(flare$imports) %>% +#' flareGr <- as_tbl_graph(flare$imports) |> #' mutate( #' type = dplyr::case_when( #' centrality_degree(mode = 'in') == 0 ~ 'Source', #' centrality_degree(mode = 'out') == 0 ~ 'Sink', #' TRUE ~ 'Both' #' ) -#' ) %>% -#' activate(edges) %>% +#' ) |> +#' activate(edges) |> #' mutate( #' type = dplyr::case_when( #' grepl('flare.analytics', paste(.N()$name[from], .N()$name[to])) ~ 'Analytics', diff --git a/R/geom_conn_bundle.R b/R/geom_conn_bundle.R index 414d67cf..8267cdae 100644 --- a/R/geom_conn_bundle.R +++ b/R/geom_conn_bundle.R @@ -58,7 +58,7 @@ #' @examples #' # Create a graph of the flare class system #' library(tidygraph) -#' flareGraph <- tbl_graph(flare$vertices, flare$edges) %>% +#' flareGraph <- tbl_graph(flare$vertices, flare$edges) |> #' mutate( #' class = map_bfs_chr(node_is_root(), .f = function(node, dist, path, ...) { #' if (dist <= 1) { diff --git a/R/geom_edge.R b/R/geom_edge.R index 28b14b0e..7ceb4964 100644 --- a/R/geom_edge.R +++ b/R/geom_edge.R @@ -2,7 +2,7 @@ #' @format NULL #' @usage NULL #' @importFrom grid segmentsGrob polylineGrob gpar -#' @importFrom dplyr %>% group_by_ do ungroup +#' @importFrom dplyr group_by_ do ungroup #' @importFrom ggforce interpolateDataFrame #' @export GeomEdgePath <- ggproto('GeomEdgePath', GeomPath, diff --git a/R/geom_edge_arc.R b/R/geom_edge_arc.R index 7b86f57e..86a3b0d1 100644 --- a/R/geom_edge_arc.R +++ b/R/geom_edge_arc.R @@ -81,14 +81,14 @@ #' @examples #' require(tidygraph) #' # Make a graph with different directions of edges -#' gr <- create_notable('Meredith') %>% -#' convert(to_directed) %>% -#' mutate(class = sample(letters[1:3], n(), replace = TRUE)) %>% -#' activate(edges) %>% +#' gr <- create_notable('Meredith') |> +#' convert(to_directed) |> +#' mutate(class = sample(letters[1:3], n(), replace = TRUE)) |> +#' activate(edges) |> #' mutate( #' class = sample(letters[1:3], n(), replace = TRUE), #' switch = sample(c(TRUE, FALSE), n(), replace = TRUE) -#' ) %>% +#' ) |> #' reroute(from = to, to = from, subset = switch) #' #' ggraph(gr, 'linear') + diff --git a/R/geom_edge_bend.R b/R/geom_edge_bend.R index d1823b09..939378f7 100644 --- a/R/geom_edge_bend.R +++ b/R/geom_edge_bend.R @@ -68,9 +68,9 @@ #' #' @examples #' require(tidygraph) -#' gr <- create_tree(20, 4) %>% -#' mutate(class = sample(letters[1:3], n(), replace = TRUE)) %>% -#' activate(edges) %>% +#' gr <- create_tree(20, 4) |> +#' mutate(class = sample(letters[1:3], n(), replace = TRUE)) |> +#' activate(edges) |> #' mutate(class = sample(letters[1:3], n(), replace = TRUE)) #' #' ggraph(gr, 'tree') + diff --git a/R/geom_edge_bundle_path.R b/R/geom_edge_bundle_path.R index 9714f4a2..69ff95d5 100644 --- a/R/geom_edge_bundle_path.R +++ b/R/geom_edge_bundle_path.R @@ -278,7 +278,7 @@ geom_edge_bundle_path0 <- function(mapping = NULL, data = get_edges(), ) ) } -#' @importFrom igraph gsize delete_edges shortest_paths get.edge.ids is_directed +#' @importFrom igraph gsize delete_edges shortest_paths is_directed as_edgelist path_bundle <- function(graph, nodes, from, to, directed = directed, max_distortion = 2, weight_fac = 2) { m <- gsize(graph) lock <- rep(FALSE, m) @@ -294,6 +294,13 @@ path_bundle <- function(graph, nodes, from, to, directed = directed, max_distort cli::cli_warn("Ignoring {.arg directed} for undirected graphs") } mode <- if (is.null(directed) || !directed) "all" else "out" + + all_edges <- as_edgelist(graph) + if (directed) { + all_edges <- paste(all_edges[,1], "-", all_edges[,2]) + } else { + all_edges <- paste(pmin(all_edges[,1], all_edges[,2]), "-", pmax(all_edges[,1], all_edges[,2])) + } # iterate for (e in edges_order) { s <- from[e] @@ -315,8 +322,15 @@ path_bundle <- function(graph, nodes, from, to, directed = directed, max_distort next() } all_edges_on_path <- rep(as.integer(path), each = 2) - all_edges_on_path <- all_edges_on_path[-c(1, length(all_edges_on_path))] - all_edges_on_path <- get.edge.ids(graph, all_edges_on_path, directed = mode != "all", error = FALSE, multi = TRUE) + all_edges_on_path <- matrix(all_edges_on_path[-c(1, length(all_edges_on_path))], ncol = 2, byrow = TRUE) + if (directed) { + all_edges_on_path <- paste(all_edges_on_path[,1], "-", all_edges_on_path[,2]) + } else { + all_edges_on_path <- paste(pmin(all_edges_on_path[,1], all_edges_on_path[,2]), + "-", + pmax(all_edges_on_path[,1], all_edges_on_path[,2])) + } + all_edges_on_path <- all_edges %in% all_edges_on_path lock[all_edges_on_path] <- TRUE paths[[e]] <- path } diff --git a/R/geom_edge_density.R b/R/geom_edge_density.R index 11def835..b7617e59 100644 --- a/R/geom_edge_density.R +++ b/R/geom_edge_density.R @@ -39,8 +39,8 @@ #' #' @examples #' require(tidygraph) -#' gr <- create_notable('bull') %>% -#' activate(edges) %>% +#' gr <- create_notable('bull') |> +#' activate(edges) |> #' mutate(class = sample(letters[1:3], n(), replace = TRUE)) #' #' ggraph(gr, 'stress') + diff --git a/R/geom_edge_diagonal.R b/R/geom_edge_diagonal.R index dea307a4..b5ebabfe 100644 --- a/R/geom_edge_diagonal.R +++ b/R/geom_edge_diagonal.R @@ -76,9 +76,9 @@ #' #' @examples #' require(tidygraph) -#' gr <- create_tree(20, 4) %>% -#' mutate(class = sample(letters[1:3], n(), replace = TRUE)) %>% -#' activate(edges) %>% +#' gr <- create_tree(20, 4) |> +#' mutate(class = sample(letters[1:3], n(), replace = TRUE)) |> +#' activate(edges) |> #' mutate(class = sample(letters[1:3], n(), replace = TRUE)) #' #' ggraph(gr, 'tree') + diff --git a/R/geom_edge_elbow.R b/R/geom_edge_elbow.R index 5ba40815..69c68ba3 100644 --- a/R/geom_edge_elbow.R +++ b/R/geom_edge_elbow.R @@ -74,10 +74,10 @@ #' #' @examples #' require(tidygraph) -#' irisDen <- hclust(dist(iris[1:4], method = 'euclidean'), method = 'ward.D2') %>% -#' as_tbl_graph() %>% -#' mutate(class = sample(letters[1:3], n(), TRUE)) %>% -#' activate(edges) %>% +#' irisDen <- hclust(dist(iris[1:4], method = 'euclidean'), method = 'ward.D2') |> +#' as_tbl_graph() |> +#' mutate(class = sample(letters[1:3], n(), TRUE)) |> +#' activate(edges) |> #' mutate(class = sample(letters[1:3], n(), TRUE)) #' #' ggraph(irisDen, 'dendrogram', circular = TRUE) + diff --git a/R/geom_edge_fan.R b/R/geom_edge_fan.R index 3cec5a8d..87078994 100644 --- a/R/geom_edge_fan.R +++ b/R/geom_edge_fan.R @@ -77,8 +77,8 @@ #' #' @examples #' require(tidygraph) -#' gr <- create_notable('bull') %>% -#' convert(to_directed) %>% +#' gr <- create_notable('bull') |> +#' convert(to_directed) |> #' bind_edges(data.frame(from = c(1, 2, 2, 3), to = c(2, 1, 3, 2))) %E>% #' mutate(class = sample(letters[1:3], 9, TRUE)) %N>% #' mutate(class = sample(c('x', 'y'), 5, TRUE)) @@ -250,17 +250,17 @@ geom_edge_fan0 <- function(mapping = NULL, data = get_edges(), ) ) } -#' @importFrom dplyr %>% group_by arrange mutate n ungroup transmute +#' @importFrom dplyr group_by arrange mutate n ungroup transmute create_fans <- function(from, to, params) { from$.id <- paste(pmin(from$from, to$to), pmax(from$from, to$to), sep = '-') from$.orig_ind <- seq_len(nrow(from)) - position <- from %>% - group_by(.data$PANEL, .data$.id) %>% - arrange(.data$from) %>% - mutate(position = seq_len(n()) - 0.5 - n() / 2) %>% - mutate(position = .data$position * ifelse(.data$from < .data$to, 1, -1)) %>% - ungroup() %>% - arrange(.data$.orig_ind) %>% + position <- from |> + group_by(.data$PANEL, .data$.id) |> + arrange(.data$from) |> + mutate(position = seq_len(n()) - 0.5 - n() / 2) |> + mutate(position = .data$position * ifelse(.data$from < .data$to, 1, -1)) |> + ungroup() |> + arrange(.data$.orig_ind) |> transmute(position = .data$position) position <- position$position max_fans <- max(table(from$.id)) diff --git a/R/geom_edge_hive.R b/R/geom_edge_hive.R index d7ee791b..53f44624 100644 --- a/R/geom_edge_hive.R +++ b/R/geom_edge_hive.R @@ -74,15 +74,15 @@ #' @examples #' # Plot the flare import graph as a hive plot #' library(tidygraph) -#' flareGr <- as_tbl_graph(flare$imports) %>% +#' flareGr <- as_tbl_graph(flare$imports) |> #' mutate( #' type = dplyr::case_when( #' centrality_degree(mode = 'in') == 0 ~ 'Source', #' centrality_degree(mode = 'out') == 0 ~ 'Sink', #' TRUE ~ 'Both' #' ) -#' ) %>% -#' activate(edges) %>% +#' ) |> +#' activate(edges) |> #' mutate( #' type = dplyr::case_when( #' grepl('flare.analytics', paste(.N()$name[from], .N()$name[to])) ~ 'Analytics', diff --git a/R/geom_edge_link.R b/R/geom_edge_link.R index 7faebb88..91551e4e 100644 --- a/R/geom_edge_link.R +++ b/R/geom_edge_link.R @@ -134,9 +134,9 @@ #' #' @examples #' require(tidygraph) -#' gr <- create_notable('bull') %>% -#' mutate(class = sample(letters[1:3], n(), replace = TRUE)) %>% -#' activate(edges) %>% +#' gr <- create_notable('bull') |> +#' mutate(class = sample(letters[1:3], n(), replace = TRUE)) |> +#' activate(edges) |> #' mutate(class = sample(letters[1:3], n(), replace = TRUE)) #' #' ggraph(gr, 'stress') + diff --git a/R/geom_edge_parallel.R b/R/geom_edge_parallel.R index 980b7049..93cdb3a5 100644 --- a/R/geom_edge_parallel.R +++ b/R/geom_edge_parallel.R @@ -68,8 +68,8 @@ #' #' @examples #' require(tidygraph) -#' gr <- create_notable('bull') %>% -#' convert(to_directed) %>% +#' gr <- create_notable('bull') |> +#' convert(to_directed) |> #' bind_edges(data.frame(from = c(1, 2, 2, 3), to = c(2, 1, 3, 2))) %E>% #' mutate(class = sample(letters[1:3], 9, TRUE)) %N>% #' mutate(class = sample(c('x', 'y'), 5, TRUE)) @@ -230,16 +230,16 @@ geom_edge_parallel0 <- function(mapping = NULL, data = get_edges(), ) ) } -#' @importFrom dplyr %>% group_by arrange summarise n ungroup pull +#' @importFrom dplyr group_by arrange summarise n ungroup pull edge_positions <- function(from, to, params) { from$.id <- paste(pmin(from$from, to$to), pmax(from$from, to$to), sep = '-') from$.orig_ind <- seq_len(nrow(from)) - from %>% - group_by(.data$PANEL, .data$.id) %>% - arrange(.data$from) %>% - mutate(position = seq_len(n()) - 0.5 - n() / 2) %>% - mutate(position = .data$position * ifelse(.data$from < .data$to, 1, -1)) %>% - ungroup() %>% - arrange(.data$.orig_ind) %>% + from |> + group_by(.data$PANEL, .data$.id) |> + arrange(.data$from) |> + mutate(position = seq_len(n()) - 0.5 - n() / 2) |> + mutate(position = .data$position * ifelse(.data$from < .data$to, 1, -1)) |> + ungroup() |> + arrange(.data$.orig_ind) |> pull(.data$position) } diff --git a/R/geom_edge_point.R b/R/geom_edge_point.R index 028db507..d0cad42e 100644 --- a/R/geom_edge_point.R +++ b/R/geom_edge_point.R @@ -42,11 +42,11 @@ #' #' @examples #' require(tidygraph) -#' gr <- create_notable('zachary') %>% -#' mutate(group = group_infomap()) %>% -#' morph(to_split, group) %>% -#' activate(edges) %>% -#' mutate(edge_group = as.character(.N()$group[1])) %>% +#' gr <- create_notable('zachary') |> +#' mutate(group = group_infomap()) |> +#' morph(to_split, group) |> +#' activate(edges) |> +#' mutate(edge_group = as.character(.N()$group[1])) |> #' unmorph() #' #' ggraph(gr, 'matrix', sort.by = node_rank_hclust()) + diff --git a/R/geom_edge_tile.R b/R/geom_edge_tile.R index 8ddefba6..85062e76 100644 --- a/R/geom_edge_tile.R +++ b/R/geom_edge_tile.R @@ -42,11 +42,11 @@ #' #' @examples #' require(tidygraph) -#' gr <- create_notable('zachary') %>% -#' mutate(group = group_infomap()) %>% -#' morph(to_split, group) %>% -#' activate(edges) %>% -#' mutate(edge_group = as.character(.N()$group[1])) %>% +#' gr <- create_notable('zachary') |> +#' mutate(group = group_infomap()) |> +#' morph(to_split, group) |> +#' activate(edges) |> +#' mutate(edge_group = as.character(.N()$group[1])) |> #' unmorph() #' #' ggraph(gr, 'matrix', sort.by = node_rank_hclust()) + diff --git a/R/geom_node_point.R b/R/geom_node_point.R index e39423ff..b50b099a 100644 --- a/R/geom_node_point.R +++ b/R/geom_node_point.R @@ -29,7 +29,7 @@ #' #' @examples #' require(tidygraph) -#' gr <- create_notable('bull') %>% +#' gr <- create_notable('bull') |> #' mutate(class = sample(letters[1:3], n(), replace = TRUE)) #' #' ggraph(gr, 'stress') + geom_node_point() diff --git a/R/geom_node_text.R b/R/geom_node_text.R index 7ff3cca4..0f458105 100644 --- a/R/geom_node_text.R +++ b/R/geom_node_text.R @@ -40,7 +40,7 @@ #' #' @examples #' require(tidygraph) -#' gr <- create_notable('bull') %>% +#' gr <- create_notable('bull') |> #' mutate(class = sample(letters[1:3], n(), replace = TRUE)) #' #' ggraph(gr, 'stress') + diff --git a/R/geom_node_tile.R b/R/geom_node_tile.R index 26ac0b30..eb93bab9 100644 --- a/R/geom_node_tile.R +++ b/R/geom_node_tile.R @@ -34,7 +34,7 @@ #' @examples #' # Create a graph of the flare class system #' library(tidygraph) -#' flareGraph <- tbl_graph(flare$vertices, flare$edges) %>% +#' flareGraph <- tbl_graph(flare$vertices, flare$edges) |> #' mutate( #' class = map_bfs_chr(node_is_root(), .f = function(node, dist, path, ...) { #' if (dist <= 1) { diff --git a/R/geom_node_voronoi.R b/R/geom_node_voronoi.R index 1603afaf..0855cff1 100644 --- a/R/geom_node_voronoi.R +++ b/R/geom_node_voronoi.R @@ -32,7 +32,7 @@ #' #' @examples #' require(tidygraph) -#' gr <- create_notable('meredith') %>% +#' gr <- create_notable('meredith') |> #' mutate(group = sample(letters[1:4], n(), TRUE)) #' #' ggraph(gr) + diff --git a/R/layout_stress.R b/R/layout_stress.R index b67597d8..7b06259d 100644 --- a/R/layout_stress.R +++ b/R/layout_stress.R @@ -38,6 +38,7 @@ #' by David Schoch #' #' @importFrom graphlayouts layout_with_stress layout_with_constrained_stress layout_with_fixed_coords +#' @importFrom igraph gorder #' @importFrom rlang eval_tidy enquo #' layout_tbl_graph_stress <- function(graph, weights = NULL, niter = 500, @@ -55,18 +56,15 @@ layout_tbl_graph_stress <- function(graph, weights = NULL, niter = 500, if (is.null(x_coord) && is.null(y_coord)) { xy <- layout_with_stress(graph, weights = weights, iter = niter, tol = tolerance, mds = mds, bbox = bbox) - } else if (!is.null(x_coord) && !is.null(y_coord)) { - xy <- cbind(x_coord, y_coord) + } else { + xy <- cbind(x_coord %||% NA, y_coord %||% NA) + if (nrow(xy) != gorder(graph)) { + cli::cli_abort("If {.arg x_coord} and/or {.arg y_coord} is given they must equal the number of nodes in the graph") + } if (anyNA(xy)) { xy <- layout_with_fixed_coords(graph, xy, weights = weights, iter = niter, tol = tolerance, mds = mds, bbox = bbox) } - } else { - dim <- if (is.null(x_coord)) "y" else "x" - coord <- if (is.null(x_coord)) y_coord else x_coord - xy <- layout_with_constrained_stress(graph, coord = coord, fixdim = dim, - weights = weights, iter = niter, - tol = tolerance, mds = mds, bbox = bbox) } nodes <- data_frame0(x = xy[,1],y = xy[,2], circular = FALSE) diff --git a/README.Rmd b/README.Rmd index 0b82e442..6a2d8c13 100644 --- a/README.Rmd +++ b/README.Rmd @@ -36,7 +36,7 @@ library(ggraph) library(tidygraph) # Create graph of highschool friendships -graph <- as_tbl_graph(highschool) %>% +graph <- as_tbl_graph(highschool) |> mutate(Popularity = centrality_degree(mode = 'in')) # plot using ggraph diff --git a/README.md b/README.md index d3c46a7f..8bc0b93a 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ library(tidygraph) #> filter # Create graph of highschool friendships -graph <- as_tbl_graph(highschool) %>% +graph <- as_tbl_graph(highschool) |> mutate(Popularity = centrality_degree(mode = 'in')) # plot using ggraph diff --git a/man/autograph.Rd b/man/autograph.Rd index 861d9cbe..b04cae3a 100644 --- a/man/autograph.Rd +++ b/man/autograph.Rd @@ -41,7 +41,7 @@ it should be created manually. } \examples{ library(tidygraph) -gr <- create_notable('herschel') \%>\% +gr <- create_notable('herschel') |> mutate(class = sample(letters[1:3], n(), TRUE)) \%E>\% mutate(weight = runif(n())) diff --git a/man/facet_graph.Rd b/man/facet_graph.Rd index 038c7889..63a6c524 100644 --- a/man/facet_graph.Rd +++ b/man/facet_graph.Rd @@ -87,7 +87,7 @@ each panel (even when nodes are not drawn). } \examples{ library(tidygraph) -gr <- as_tbl_graph(highschool) \%>\% +gr <- as_tbl_graph(highschool) |> mutate(popularity = as.character(cut(centrality_degree(mode = 'in'), breaks = 3, labels = c('low', 'medium', 'high') diff --git a/man/facet_nodes.Rd b/man/facet_nodes.Rd index 82b08391..35e348e0 100644 --- a/man/facet_nodes.Rd +++ b/man/facet_nodes.Rd @@ -77,7 +77,7 @@ panel. } \examples{ library(tidygraph) -gr <- as_tbl_graph(highschool) \%>\% +gr <- as_tbl_graph(highschool) |> mutate(popularity = as.character(cut(centrality_degree(mode = 'in'), breaks = 3, labels = c('low', 'medium', 'high') diff --git a/man/geom_axis_hive.Rd b/man/geom_axis_hive.Rd index 0b9ec3be..73a6ea86 100644 --- a/man/geom_axis_hive.Rd +++ b/man/geom_axis_hive.Rd @@ -78,15 +78,15 @@ geom_axis_hive understand the following aesthetics. \examples{ # Plot the flare import graph as a hive plot library(tidygraph) -flareGr <- as_tbl_graph(flare$imports) \%>\% +flareGr <- as_tbl_graph(flare$imports) |> mutate( type = dplyr::case_when( centrality_degree(mode = 'in') == 0 ~ 'Source', centrality_degree(mode = 'out') == 0 ~ 'Sink', TRUE ~ 'Both' ) - ) \%>\% - activate(edges) \%>\% + ) |> + activate(edges) |> mutate( type = dplyr::case_when( grepl('flare.analytics', paste(.N()$name[from], .N()$name[to])) ~ 'Analytics', diff --git a/man/geom_conn_bundle.Rd b/man/geom_conn_bundle.Rd index 17040dcb..67a1785c 100644 --- a/man/geom_conn_bundle.Rd +++ b/man/geom_conn_bundle.Rd @@ -119,7 +119,7 @@ automatically set, but can be overwritten. \examples{ # Create a graph of the flare class system library(tidygraph) -flareGraph <- tbl_graph(flare$vertices, flare$edges) \%>\% +flareGraph <- tbl_graph(flare$vertices, flare$edges) |> mutate( class = map_bfs_chr(node_is_root(), .f = function(node, dist, path, ...) { if (dist <= 1) { diff --git a/man/geom_edge_arc.Rd b/man/geom_edge_arc.Rd index 08304490..410e06c5 100644 --- a/man/geom_edge_arc.Rd +++ b/man/geom_edge_arc.Rd @@ -255,14 +255,14 @@ automatically be renamed appropriately. \examples{ require(tidygraph) # Make a graph with different directions of edges -gr <- create_notable('Meredith') \%>\% - convert(to_directed) \%>\% - mutate(class = sample(letters[1:3], n(), replace = TRUE)) \%>\% - activate(edges) \%>\% +gr <- create_notable('Meredith') |> + convert(to_directed) |> + mutate(class = sample(letters[1:3], n(), replace = TRUE)) |> + activate(edges) |> mutate( class = sample(letters[1:3], n(), replace = TRUE), switch = sample(c(TRUE, FALSE), n(), replace = TRUE) - ) \%>\% + ) |> reroute(from = to, to = from, subset = switch) ggraph(gr, 'linear') + diff --git a/man/geom_edge_bend.Rd b/man/geom_edge_bend.Rd index 3690cc5a..979554b9 100644 --- a/man/geom_edge_bend.Rd +++ b/man/geom_edge_bend.Rd @@ -243,9 +243,9 @@ automatically be renamed appropriately. \examples{ require(tidygraph) -gr <- create_tree(20, 4) \%>\% - mutate(class = sample(letters[1:3], n(), replace = TRUE)) \%>\% - activate(edges) \%>\% +gr <- create_tree(20, 4) |> + mutate(class = sample(letters[1:3], n(), replace = TRUE)) |> + activate(edges) |> mutate(class = sample(letters[1:3], n(), replace = TRUE)) ggraph(gr, 'tree') + diff --git a/man/geom_edge_density.Rd b/man/geom_edge_density.Rd index 33f0dfa9..cf2e5925 100644 --- a/man/geom_edge_density.Rd +++ b/man/geom_edge_density.Rd @@ -81,8 +81,8 @@ automatically be renamed appropriately. \examples{ require(tidygraph) -gr <- create_notable('bull') \%>\% - activate(edges) \%>\% +gr <- create_notable('bull') |> + activate(edges) |> mutate(class = sample(letters[1:3], n(), replace = TRUE)) ggraph(gr, 'stress') + diff --git a/man/geom_edge_diagonal.Rd b/man/geom_edge_diagonal.Rd index 9f329e9d..e21da91e 100644 --- a/man/geom_edge_diagonal.Rd +++ b/man/geom_edge_diagonal.Rd @@ -247,9 +247,9 @@ automatically be renamed appropriately. \examples{ require(tidygraph) -gr <- create_tree(20, 4) \%>\% - mutate(class = sample(letters[1:3], n(), replace = TRUE)) \%>\% - activate(edges) \%>\% +gr <- create_tree(20, 4) |> + mutate(class = sample(letters[1:3], n(), replace = TRUE)) |> + activate(edges) |> mutate(class = sample(letters[1:3], n(), replace = TRUE)) ggraph(gr, 'tree') + diff --git a/man/geom_edge_elbow.Rd b/man/geom_edge_elbow.Rd index 6390a9fd..0da0063d 100644 --- a/man/geom_edge_elbow.Rd +++ b/man/geom_edge_elbow.Rd @@ -248,10 +248,10 @@ automatically be renamed appropriately. \examples{ require(tidygraph) -irisDen <- hclust(dist(iris[1:4], method = 'euclidean'), method = 'ward.D2') \%>\% - as_tbl_graph() \%>\% - mutate(class = sample(letters[1:3], n(), TRUE)) \%>\% - activate(edges) \%>\% +irisDen <- hclust(dist(iris[1:4], method = 'euclidean'), method = 'ward.D2') |> + as_tbl_graph() |> + mutate(class = sample(letters[1:3], n(), TRUE)) |> + activate(edges) |> mutate(class = sample(letters[1:3], n(), TRUE)) ggraph(irisDen, 'dendrogram', circular = TRUE) + diff --git a/man/geom_edge_fan.Rd b/man/geom_edge_fan.Rd index c2228201..2c8168e1 100644 --- a/man/geom_edge_fan.Rd +++ b/man/geom_edge_fan.Rd @@ -249,8 +249,8 @@ automatically be renamed appropriately. \examples{ require(tidygraph) -gr <- create_notable('bull') \%>\% - convert(to_directed) \%>\% +gr <- create_notable('bull') |> + convert(to_directed) |> bind_edges(data.frame(from = c(1, 2, 2, 3), to = c(2, 1, 3, 2))) \%E>\% mutate(class = sample(letters[1:3], 9, TRUE)) \%N>\% mutate(class = sample(c('x', 'y'), 5, TRUE)) diff --git a/man/geom_edge_hive.Rd b/man/geom_edge_hive.Rd index a07559ee..8e221cbe 100644 --- a/man/geom_edge_hive.Rd +++ b/man/geom_edge_hive.Rd @@ -245,15 +245,15 @@ automatically be renamed appropriately. \examples{ # Plot the flare import graph as a hive plot library(tidygraph) -flareGr <- as_tbl_graph(flare$imports) \%>\% +flareGr <- as_tbl_graph(flare$imports) |> mutate( type = dplyr::case_when( centrality_degree(mode = 'in') == 0 ~ 'Source', centrality_degree(mode = 'out') == 0 ~ 'Sink', TRUE ~ 'Both' ) - ) \%>\% - activate(edges) \%>\% + ) |> + activate(edges) |> mutate( type = dplyr::case_when( grepl('flare.analytics', paste(.N()$name[from], .N()$name[to])) ~ 'Analytics', diff --git a/man/geom_edge_link.Rd b/man/geom_edge_link.Rd index 0ca72b18..e83093e4 100644 --- a/man/geom_edge_link.Rd +++ b/man/geom_edge_link.Rd @@ -228,9 +228,9 @@ aesthetics. \examples{ require(tidygraph) -gr <- create_notable('bull') \%>\% - mutate(class = sample(letters[1:3], n(), replace = TRUE)) \%>\% - activate(edges) \%>\% +gr <- create_notable('bull') |> + mutate(class = sample(letters[1:3], n(), replace = TRUE)) |> + activate(edges) |> mutate(class = sample(letters[1:3], n(), replace = TRUE)) ggraph(gr, 'stress') + diff --git a/man/geom_edge_parallel.Rd b/man/geom_edge_parallel.Rd index 119a2d6e..6737bede 100644 --- a/man/geom_edge_parallel.Rd +++ b/man/geom_edge_parallel.Rd @@ -237,8 +237,8 @@ automatically be renamed appropriately. \examples{ require(tidygraph) -gr <- create_notable('bull') \%>\% - convert(to_directed) \%>\% +gr <- create_notable('bull') |> + convert(to_directed) |> bind_edges(data.frame(from = c(1, 2, 2, 3), to = c(2, 1, 3, 2))) \%E>\% mutate(class = sample(letters[1:3], 9, TRUE)) \%N>\% mutate(class = sample(c('x', 'y'), 5, TRUE)) diff --git a/man/geom_edge_point.Rd b/man/geom_edge_point.Rd index de6533c6..29dafda0 100644 --- a/man/geom_edge_point.Rd +++ b/man/geom_edge_point.Rd @@ -73,11 +73,11 @@ automatically be renamed appropriately. \examples{ require(tidygraph) -gr <- create_notable('zachary') \%>\% - mutate(group = group_infomap()) \%>\% - morph(to_split, group) \%>\% - activate(edges) \%>\% - mutate(edge_group = as.character(.N()$group[1])) \%>\% +gr <- create_notable('zachary') |> + mutate(group = group_infomap()) |> + morph(to_split, group) |> + activate(edges) |> + mutate(edge_group = as.character(.N()$group[1])) |> unmorph() ggraph(gr, 'matrix', sort.by = node_rank_hclust()) + diff --git a/man/geom_edge_tile.Rd b/man/geom_edge_tile.Rd index 4f9d48a6..5d537eee 100644 --- a/man/geom_edge_tile.Rd +++ b/man/geom_edge_tile.Rd @@ -73,11 +73,11 @@ automatically be renamed appropriately. \examples{ require(tidygraph) -gr <- create_notable('zachary') \%>\% - mutate(group = group_infomap()) \%>\% - morph(to_split, group) \%>\% - activate(edges) \%>\% - mutate(edge_group = as.character(.N()$group[1])) \%>\% +gr <- create_notable('zachary') |> + mutate(group = group_infomap()) |> + morph(to_split, group) |> + activate(edges) |> + mutate(edge_group = as.character(.N()$group[1])) |> unmorph() ggraph(gr, 'matrix', sort.by = node_rank_hclust()) + diff --git a/man/geom_node_point.Rd b/man/geom_node_point.Rd index 9fde81c2..77367a6c 100644 --- a/man/geom_node_point.Rd +++ b/man/geom_node_point.Rd @@ -71,7 +71,7 @@ automatically set, but can be overwritten. \examples{ require(tidygraph) -gr <- create_notable('bull') \%>\% +gr <- create_notable('bull') |> mutate(class = sample(letters[1:3], n(), replace = TRUE)) ggraph(gr, 'stress') + geom_node_point() diff --git a/man/geom_node_text.Rd b/man/geom_node_text.Rd index a2d8e49c..28673d4c 100644 --- a/man/geom_node_text.Rd +++ b/man/geom_node_text.Rd @@ -117,7 +117,7 @@ not set by default \examples{ require(tidygraph) -gr <- create_notable('bull') \%>\% +gr <- create_notable('bull') |> mutate(class = sample(letters[1:3], n(), replace = TRUE)) ggraph(gr, 'stress') + diff --git a/man/geom_node_tile.Rd b/man/geom_node_tile.Rd index efbf7d3d..433af3d8 100644 --- a/man/geom_node_tile.Rd +++ b/man/geom_node_tile.Rd @@ -76,7 +76,7 @@ automatically set, but can be overwritten. \examples{ # Create a graph of the flare class system library(tidygraph) -flareGraph <- tbl_graph(flare$vertices, flare$edges) \%>\% +flareGraph <- tbl_graph(flare$vertices, flare$edges) |> mutate( class = map_bfs_chr(node_is_root(), .f = function(node, dist, path, ...) { if (dist <= 1) { diff --git a/man/geom_node_voronoi.Rd b/man/geom_node_voronoi.Rd index d50ba31b..1674d0f5 100644 --- a/man/geom_node_voronoi.Rd +++ b/man/geom_node_voronoi.Rd @@ -114,7 +114,7 @@ automatically set, but can be overwritten. \examples{ require(tidygraph) -gr <- create_notable('meredith') \%>\% +gr <- create_notable('meredith') |> mutate(group = sample(letters[1:4], n(), TRUE)) ggraph(gr) + diff --git a/vignettes/Edges.Rmd b/vignettes/Edges.Rmd index 1a90fe40..9904f8de 100644 --- a/vignettes/Edges.Rmd +++ b/vignettes/Edges.Rmd @@ -43,7 +43,7 @@ library(purrr) library(rlang) set_graph_style(plot_margin = margin(1,1,1,1)) -hierarchy <- as_tbl_graph(hclust(dist(iris[, 1:4]))) %>% +hierarchy <- as_tbl_graph(hclust(dist(iris[, 1:4]))) |> mutate(Class = map_bfs_back_chr(node_is_root(), .f = function(node, path, ...) { if (leaf[node]) { as.character(iris$Species[as.integer(label[node])]) @@ -57,10 +57,10 @@ hierarchy <- as_tbl_graph(hclust(dist(iris[, 1:4]))) %>% } })) -hairball <- as_tbl_graph(highschool) %>% +hairball <- as_tbl_graph(highschool) |> mutate( year_pop = map_local(mode = 'in', .f = function(neighborhood, ...) { - neighborhood %E>% pull(year) %>% table() %>% sort(decreasing = TRUE) + neighborhood %E>% pull(year) |> table() |> sort(decreasing = TRUE) }), pop_devel = map_chr(year_pop, function(pop) { if (length(pop) == 0 || length(unique(pop)) == 1) return('unchanged') @@ -69,8 +69,8 @@ hairball <- as_tbl_graph(highschool) %>% '1958' = 'increased') }), popularity = map_dbl(year_pop, ~ .[1]) %|% 0 - ) %>% - activate(edges) %>% + ) |> + activate(edges) |> mutate(year = as.character(year)) ``` @@ -119,7 +119,7 @@ Loops cannot be shown with regular edges as they have no length. A dedicated ```{r} # let's make some of the student love themselves -loopy_hairball <- hairball %>% +loopy_hairball <- hairball |> bind_edges(tibble::tibble(from = 1:5, to = 1:5, year = rep('1957', 5))) ggraph(loopy_hairball, layout = 'stress') + geom_edge_link(aes(colour = year), alpha = 0.25) + @@ -167,6 +167,54 @@ ggraph(hairball, layout = 'linear', circular = TRUE) + coord_fixed() ``` +### Bundling +Edge bundling is a technique to reduce clutter in a network visualization by +bundling edges that flows in the same direction. There are various ways of doing +this, many with heavy computational cost and the potential to mislead. The +technique were initially confined to connections between nodes with a +[hierarchical structure](#Connections) but has been expanded to general graphs. +ggraph provides 3 different bundling geoms with various up- and downsides. + +#### Force directed +This is perhaps the most classic. It treats the edges as an array of points with +the propensity to attract each other if edges are parallel. It suffers from bad +performance (though the edge bundling geoms uses memoisation to avoid +recomputations) and can also be misleading as it doesn't use the underlying +topology of the graph to determine if edges should be bundled, only whether they +are parallel. + +```{r} +ggraph(hairball) + + geom_edge_bundle_force(n_cycle = 2, threshold = 0.4) +``` + +#### Edge path +An alternative is to let the edges follow the shortest paths rather than +attract each other. This means the topology is being used in the bundling and +in theory lead to less misleading results. It also has the upside of being +faster. The algorithm is iterative so that if an edge has been bundled it is +deleted from the graph where the shortest path is being searched in. In this +way the edges naturally converge towards a few "highways". + +```{r} +ggraph(hairball) + + geom_edge_bundle_path() +``` + +#### Minimal +In the same vein as edge path bundling but even simpler, you can use the minimal +spanning tree of the graph as the scaffold to bundle edges along. As such, it +changes to the hierarchical edge bundling approach, just with an implicit +hierarchy calculated on the graph. This method is very fast but does create bias +in the output as edges will (obviously) travel along the minimal spanning tree +thus amplifying that topology. + +```{r} +ggraph(hairball) + + geom_edge_bundle_minimal() +``` + + ### Elbow Aah... The classic dendrogram with its right angle bends. Of course such visualizations are also supported with the `geom_edge_elbow()`. It goes without @@ -321,9 +369,9 @@ see the effect here we will use a slightly simpler graph ```{r} # Random names - I swear -simple <- create_notable('bull') %>% - mutate(name = c('Thomas', 'Bob', 'Hadley', 'Winston', 'Baptiste')) %>% - activate(edges) %>% +simple <- create_notable('bull') |> + mutate(name = c('Thomas', 'Bob', 'Hadley', 'Winston', 'Baptiste')) |> + activate(edges) |> mutate(type = sample(c('friend', 'foe'), 5, TRUE)) ``` diff --git a/vignettes/Layouts.Rmd b/vignettes/Layouts.Rmd index 390ed8f7..f8def4e8 100644 --- a/vignettes/Layouts.Rmd +++ b/vignettes/Layouts.Rmd @@ -176,7 +176,7 @@ Below is a sample of some of the layouts available through `igraph` applied to the highschool graph. ```{r, fig.show='hold', results='hide'} -graph <- as_tbl_graph(highschool) %>% +graph <- as_tbl_graph(highschool) |> mutate(degree = centrality_degree()) lapply(c('stress', 'fr', 'lgl', 'graphopt'), function(layout) { ggraph(graph, layout = layout) + @@ -186,6 +186,13 @@ lapply(c('stress', 'fr', 'lgl', 'graphopt'), function(layout) { }) ``` +The default plot is the `"stress"` layout that uses stress majorization to +spread out nodes. It generally does a good job and is deterministic so that it +doesn't change upon every call (many other layouts does that as they use +randomisation for the initial node positions). The stress layout also makes it +possible to fix the location of certain nodes in one or two dimensions making it +a very versatile starting point for your visualisation. + ### Hive plots A hive plot, while still technically a node-edge diagram, is a bit different from the rest as it uses information pertaining to the nodes, rather than the @@ -195,7 +202,7 @@ graph structure. They are less common though, so use will often require some additional explanation. ```{r} -graph <- graph %>% +graph <- graph |> mutate(friends = ifelse( centrality_degree(mode = 'in') < 5, 'few', ifelse(centrality_degree(mode = 'in') >= 15, 'many', 'medium') @@ -314,8 +321,8 @@ implicitly position a node at the root. To avoid that you can use the `unrooted` layout instead. ```{r} -tree <- create_tree(100, 2, directed = FALSE) %>% - activate(edges) %>% +tree <- create_tree(100, 2, directed = FALSE) |> + activate(edges) |> mutate(length = runif(n())) ggraph(tree, 'unrooted', length = length) + geom_edge_link() @@ -377,6 +384,39 @@ ggraph(graph, 'fabric', sort.by = node_rank_fabric(), shadow.edges =TRUE) + coord_fixed() ``` +### Spatial layouts +ggraph has a few layouts aimed at data where the node has a physical location. +You may think this requires no layout at all and to some extent you are correct. + +ggraph supports spatial networks through the sfnetworks package and grabs the +node position and CRS from the data correctly when using the "sf" layout: + +```{r} +gr <- sfnetworks::as_sfnetwork(sfnetworks::roxel) + +ggraph(gr, 'sf') + + geom_edge_sf(aes(color = type)) + + geom_node_sf(size = 0.3) +``` + +Another type of spatial layout is the metro layout which takes in a prior +position and attempts to make these positions fit into a grid-like structure, +well known in railroad and metro maps. + +```{r} +gr <- create_notable('Walther') +# Use stress layout to come up with a initial position +prior <- create_layout(gr, 'stress') + +# Optimise placement with metro layout +ggraph(gr, 'metro', x = prior$x, y = prior$y, grid_space = 1, max_movement = 50) + + geom_edge_link(width = 4) + + geom_node_point(size = 10) + + geom_edge_link(color = 'white', width = 1) + + geom_node_point(color = 'white', size = 4) +``` + + ## Want more? Check out the other vignettes for more information on how to draw [nodes](Nodes.html) and [edges](Edges.html)... diff --git a/vignettes/Nodes.Rmd b/vignettes/Nodes.Rmd index 9a2ed889..b9ae0943 100644 --- a/vignettes/Nodes.Rmd +++ b/vignettes/Nodes.Rmd @@ -105,7 +105,7 @@ from a voronoi tesselation. This is useful for e.g. showing dominance of certain node types in an area as overlapping is avoided: ```{r} -graph <- create_notable('meredith') %>% +graph <- create_notable('meredith') |> mutate(group = sample(c('A', 'B'), n(), TRUE)) ggraph(graph, 'stress') +