Nothing
#' Get paths from a specified node in a directed graph
#'
#' @description
#'
#' Obtain a list of all possible paths from a given node within a directed
#' graph.
#'
#' @inheritParams render_graph
#' @param from The node from which all paths will be determined.
#' @param to The node to which all paths will be determined.
#' @param shortest_path An option to return paths that are the shortest in the
#' set of all determined paths.
#' @param longest_path An option to return paths that are the longest in the set
#' of all determined paths.
#' @param distance A vector of integer values that specify which of the valid
#' paths to return when filtering by distance.
#'
#' @return A list of paths, sorted by ascending traversal length, comprising
#' vectors of node IDs in sequence of traversal through the graph.
#'
#' @examples
#' # Create a simple graph
#' graph <-
#' create_graph() %>%
#' add_n_nodes(n = 8) %>%
#' add_edge(from = 1, to = 2) %>%
#' add_edge(from = 1, to = 3) %>%
#' add_edge(from = 3, to = 4) %>%
#' add_edge(from = 3, to = 5) %>%
#' add_edge(from = 4, to = 6) %>%
#' add_edge(from = 2, to = 7) %>%
#' add_edge(from = 7, to = 5) %>%
#' add_edge(from = 4, to = 8)
#'
#' # Get a list of all paths outward from node `1`
#' graph %>%
#' get_paths(from = 1)
#'
#' # Get a list of all paths leading to node `6`
#' graph %>%
#' get_paths(to = 6)
#'
#' # Get a list of all paths from `1` to `5`
#' graph %>%
#' get_paths(
#' from = 1,
#' to = 5)
#'
#' # Get a list of all paths from `1` up to a distance
#' # of 2 node traversals
#' graph %>%
#' get_paths(
#' from = 1,
#' distance = 2)
#'
#' # Get a list of the shortest paths from `1` to `5`
#' get_paths(
#' graph,
#' from = 1,
#' to = 5,
#' shortest_path = TRUE)
#'
#' # Get a list of the longest paths from `1` to `5`
#' get_paths(
#' graph,
#' from = 1,
#' to = 5,
#' longest_path = TRUE)
#'
#' @export
get_paths <- function(
graph,
from = NULL,
to = NULL,
shortest_path = FALSE,
longest_path = FALSE,
distance = NULL
) {
# Validation: Graph object is valid
check_graph_valid(graph)
reverse_paths <- FALSE
if (is.null(from) && !is.null(to)) {
from_switch <- graph$edges_df$from
to_switch <- graph$edges_df$to
graph$edges_df$from <- to_switch
graph$edges_df$to <- from_switch
from <- to
to <- NULL
reverse_paths <- TRUE
}
for (m in seq_along(from)) {
if (m == 1) all_paths <- list()
# Initialize paths with starting node
paths <- list(from[m])
repeat {
for (i in seq_along(paths)) {
if (!all(is.na(
get_successors(
graph,
paths[[i]][
length(paths[[i]])])))) {
# Get the successors for the last node
# in the given path
next_nodes <-
get_successors(
graph,
paths[[i]][
length(paths[[i]])])
# Filter next_nodes if cycles are detected
next_nodes <-
next_nodes[
which(!(
next_nodes %in%
paths[[i]][
seq_along(length(paths[[i]])) - 1]))]
# Apply traversed nodes to each of the
# path vectors in a multiple degree context
if (length(next_nodes) > 1) {
for (j in seq_along(next_nodes)) {
if (j == 1) paths[[i]] <-
c(paths[[i]], next_nodes[1])
if (j > 1) paths[[length(paths) + 1]] <-
c(paths[[i]][
-length(paths[[i]])],
next_nodes[j])
}
}
# Apply traversed nodes to each of the
# path vectors in a single degree context
if (length(next_nodes) == 1) {
paths[[i]] <- c(paths[[i]], next_nodes[1])
}
}
}
# Check each node visited in the present
# iteration for whether their traversals
# should end
for (k in seq_along(paths)) {
if (k == 1) check <- vector()
check <-
c(check,
any(is.na(
get_successors(
graph,
paths[[k]][length(paths[[k]])])) |
all(get_successors(
graph,
paths[[k]][length(paths[[k]])]) %in%
paths[[k]])))
# Remove nodes from vectors within paths
# if they were previously traversed
if (paths[[k]][length(paths[[k]])] %in%
paths[[k]][seq_along(paths[[k]]) - 1]) {
paths[[k]] <- paths[[k]][-length(paths[[k]])]
}
}
if (all(check)) break
}
if (!(length(paths) == 1 && is.na(paths[1]))) {
all_paths <- c(all_paths, paths)
}
if (m == length(from)) paths <- all_paths
}
# Arrange vectors in list in order of
# increasing length
order <- sapply(seq_along(paths),
function(x) length(paths[[x]]))
names(order) <- seq_along(paths)
order <- sort(order)
order <- as.numeric(names(order))
paths <- paths[order]
# If only a single vector returned, return NA
if (length(paths) == 1 && length(paths[[1]]) == 1) {
return(NA)
}
# Remove paths of single length
for (i in seq_along(paths)) {
if (i == 1) {
single_length_paths <- vector(mode = "numeric")
}
if (length(paths[[i]]) == 1) {
single_length_paths <- c(single_length_paths, i)
}
if (i == length(paths)) {
paths[single_length_paths] <- NULL
}
}
# If only `from` but not the `to` node specified,
# get paths that consider the filtering criteria
if (!is.null(from) && is.null(to)) {
# Filter the `path_lengths` vector
# by the chosen criteria
if (shortest_path && !longest_path) {
# Remove paths not of shortest length
for (i in seq_along(paths)) {
if (i == 1) {
not_shortest_length_paths <-
vector(mode = "numeric")
shortest_length <-
min(sapply(seq_along(paths),
function(x) length(paths[[x]])))
}
if (length(paths[[i]]) != shortest_length) {
not_shortest_length_paths <-
c(not_shortest_length_paths, i)
}
if (i == length(paths)) {
paths[not_shortest_length_paths] <- NULL
}
}
} else if (!shortest_path && longest_path) {
# Remove paths not of longest length
for (i in seq_along(paths)) {
if (i == 1) {
not_longest_length_paths <-
vector(mode = "numeric")
longest_length <-
max(sapply(seq_along(paths),
function(x) length(paths[[x]])))
}
if (length(paths[[i]]) != longest_length) {
not_longest_length_paths <-
c(not_longest_length_paths, i)
}
if (i == length(paths)) {
paths[not_longest_length_paths] <- NULL
}
}
} else if (!is.null(distance)) {
# Remove paths not of the specified distances
for (i in seq_along(paths)) {
if (i == 1) {
not_specified_length_paths <-
vector(mode = "numeric")
specified_lengths <- distance
}
if (length(paths[[i]]) <
specified_lengths + 1) {
not_specified_length_paths <-
c(not_specified_length_paths, i)
}
if (i == length(paths)) {
paths[not_specified_length_paths] <- NULL
}
}
# Trim paths to specified distance
for (i in seq_along(paths)) {
paths[[i]] <-
paths[[i]][seq_len(specified_lengths + 1)]
}
# Create a unique list of paths
paths <- unique(paths)
}
}
# If both a 'from' node and a 'to' node specified,
# get paths that begin and end with those nodes
if (!is.null(from) && !is.null(to)) {
# Determine which paths contain the 2nd node target
paths_with_end_node <-
which(sapply(seq_along(paths),
function(x) to %in% paths[[x]]))
if (length(paths_with_end_node) == 0) {
return(NA)
}
# Obtain a vector of path lengths for each of
# the valid paths
path_lengths <-
sapply(paths_with_end_node,
function(x) which(paths[[x]] %in% to))
# Apply list indices as names for the
# `paths_lengths` vector
names(path_lengths) <- paths_with_end_node
# Filter the `path_lengths` vector by the
# chosen criteria
if (shortest_path && !longest_path) {
path_lengths <-
as.numeric(names(path_lengths[
which(path_lengths == min(path_lengths))]))
} else if (!shortest_path && longest_path) {
path_lengths <-
as.numeric(names(path_lengths[
which(path_lengths == max(path_lengths))]))
} else if (!is.null(distance)) {
path_lengths <-
as.numeric(names(path_lengths[
which(path_lengths %in% distance)]))
} else if (is.null(distance) && !shortest_path && !longest_path) {
path_lengths <- as.numeric(names(path_lengths))
}
# If there are no paths that match the chosen
# criteria, return NA
if (length(path_lengths) == 0) {
return(NA)
}
paths <- paths[path_lengths]
# Modify `paths` list of vectors such that nodes
# at the beginning and end of each vector are the
# `from` and `to` nodes
for (i in seq_along(paths)) {
paths[[i]] <-
paths[[i]][1:which(paths[[i]] == to)]
}
}
if (reverse_paths) {
for (i in seq_along(paths)) {
paths[[i]] <- rev(paths[[i]])
}
}
paths
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.