diff --git a/.gitignore b/.gitignore index 192c45a..4d395ec 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ /CRAN-RELEASE inst/doc /.DS_Store +/data diff --git a/DESCRIPTION b/DESCRIPTION index fc804b9..198d578 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -27,7 +27,8 @@ Imports: Matrix, Suggests: igraph, knitr, - rmarkdown + rmarkdown, + tidygraph Encoding: UTF-8 LazyData: true RoxygenNote: 7.1.1 diff --git a/NAMESPACE b/NAMESPACE index 8f8a63e..bef430d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -16,6 +16,11 @@ S3method(get_adjacency,igraph) S3method(get_adjacency,matrix) S3method(get_adjacency,sparseMatrix) S3method(get_adjacency,tbl) +S3method(get_adjacency,tbl_graph) +S3method(get_rolling_windows,default) +S3method(get_rolling_windows,igraph) +S3method(get_rolling_windows,tbl) +S3method(get_rolling_windows,tbl_graph) S3method(get_transition,default) S3method(get_transition,dgCMatrix) S3method(get_transition,dgTMatrix) diff --git a/R/adjacency_matrix.R b/R/adjacency_matrix.R index 9401ccc..e64d17a 100644 --- a/R/adjacency_matrix.R +++ b/R/adjacency_matrix.R @@ -37,6 +37,16 @@ get_adjacency.tbl <- function(x, select_cols = NULL, multiedge = NULL, aggr_expr drop_names = drop_names, directed = directed, selfloops = selfloops) } +#' @rdname get_adjacency +#' @export +get_adjacency.tbl_graph <- function(x, select_cols = NULL, multiedge = NULL, aggr_expression = NULL, nodes = NULL, sparse = TRUE, + drop_names = FALSE, directed = NULL, selfloops = NULL, edgelist = NULL, ...){ + if(is.null(multiedge)) multiedge <- FALSE + x %>% tidygraph::activate('edges') %>% dplyr::as_tibble() %>% + el2adj(select_cols = select_cols, multiedge = multiedge, aggr_expression = aggr_expression, nodes = nodes, sparse = sparse, + drop_names = drop_names, directed = directed, selfloops = selfloops) +} + #' @rdname get_adjacency #' @export get_adjacency.data.frame <- function(x, select_cols = NULL, multiedge = NULL, aggr_expression = NULL, nodes = NULL, sparse = TRUE, diff --git a/R/temporal_nets.R b/R/temporal_nets.R index 00ca47e..8562c73 100644 --- a/R/temporal_nets.R +++ b/R/temporal_nets.R @@ -4,8 +4,9 @@ #' Builds rolling multiedge time-window networks from the edge_list. #' -#' @param edge.list A data-frame with at least 3 columns: "source", "target", -#' "timestamp", where "from" and "to" are vertex identifiers, and "time" is a +#' @param edge.list A data-frame, tibble, with at least 3 columns: "source", +#' "target", "timestamp", or a graph object that can be coerced to such table, +#' where "from" and "to" are vertex identifiers, and "time" is a #' Unix-timestamp when the edge occurred. Alternatively, the user can select #' which column is which passing `select_cols` #' @param window_size The duration of each time-window network given as the @@ -29,9 +30,9 @@ #' @param as_date (optional) boolean identifying if timestamps are in date #' formats, or unix seconds. #' @param out_format character vector specifying the format of the output: -#' 'edgelist' for edgelists, 'adjacency' for adjacency matrices, `igraph` for -#' igraph graphs. Defaults to 'edgelist'. In the case of igraph graphs, adding -#' `igraph_weighted` allows to generated weighted graphs. +#' 'edgelist' for edgelists, 'adjacency' for adjacency matrices, 'tbl_graph' for `tbl_graph`s, 'igraph' for +#' `igraph` graphs. Defaults to 'edgelist'. In the case of igraph and tbl_graph graphs, adding +#' '_weighted' (as in 'igraph_weighted') allows to generate weighted graphs. #' @param ... additional parameters passed to internal constructors. E.g., to #' `get_adjacency`. #' @param ncores integer, number of cores to use. Defaults to 1. @@ -53,6 +54,26 @@ get_rolling_windows <- function(edge.list, as_date = NULL, ncores = NULL, ...) { + UseMethod("get_rolling_windows") +} + +#' @rdname get_rolling_windows +#' @param nodes_tbl (optional) tibble containing node attributes to be added to +#' tbl_graph objects when tbl_graph output is chosen. The tibble should have +#' a node_key column matching node names used in the edge.list. +#' @export +get_rolling_windows.tbl <- function(edge.list, + window_size, + step_size = NULL, + start_time = NULL, + end_time = NULL, + out_format = 'edgelist', + flush = "earliest", + select_cols = NULL, + as_date = NULL, + ncores = NULL, + nodes_tbl = NULL, + ...) { # Builds rolling multiedge time-window networks from the edge_list. # # Args: @@ -87,7 +108,11 @@ get_rolling_windows <- function(edge.list, col_names <- .select_cols_temporal(col_names = col_names, select_cols = select_cols, attr_cols = length(select_cols)==4) colnames(edge.list) <- col_names - edge.list %>% select(source,target,timestamp,attr) -> edge.list + if(length(select_cols)>3){ + edge.list %>% select('source','target','timestamp','attr') -> edge.list + } else{ + edge.list %>% select('source','target','timestamp') -> edge.list + } if(is.null(start_time)) start_time <- min(edge.list$timestamp) if(is.null(end_time)) end_time <- max(edge.list$timestamp) @@ -136,22 +161,43 @@ get_rolling_windows <- function(edge.list, .el2slice(start_time = lower, end_time = upper, index = FALSE) -> curr_window } + if(grepl('graph', out_format)){ + if(requireNamespace("tidygraph", quietly = TRUE)){ + if(is.null(nodes_tbl)){ + edge.list %>% + .el2slice(start_time = lower, end_time = upper, index = FALSE) %>% + conditional_makeweighted(select_cols = select_cols, out_format=out_format) %>% + tidygraph::as_tbl_graph() -> + curr_window + } else{ + edge.list %>% + .el2slice(start_time = lower, end_time = upper, index = FALSE) %>% + conditional_makeweighted(select_cols = select_cols, out_format=out_format) %>% + tidygraph::as_tbl_graph() %>% + tidygraph::activate('nodes') %>% + rename(node_key = 'name') %>% + inner_join(nodes_tbl) %>% + dplyr::select(-'node_key') -> + curr_window + } + } + } if(grepl('adj', out_format)){ edge.list %>% .el2slice(start_time = lower, end_time = upper, index = FALSE) %>% get_adjacency(select_cols = select_cols, multiedge = TRUE, ...) -> curr_window } - if(grepl('igraph', out_format)){ - weighted <- NULL - if(grepl('weight', out_format)) weighted <- TRUE - if(requireNamespace("igraph", quietly = TRUE)){ - edge.list %>% - .el2slice(start_time = lower, end_time = upper, index = FALSE) %>% - igraph::graph_from_adjacency_matrix(weighted = weighted) -> - curr_window - } - } + # if(grepl('igraph', out_format)){ + # weighted <- NULL + # if(grepl('weight', out_format)) weighted <- TRUE + # if(requireNamespace("igraph", quietly = TRUE)){ + # edge.list %>% + # .el2slice(start_time = lower, end_time = upper, index = FALSE) %>% + # igraph::graph_from_adjacency_matrix(weighted = weighted) -> + # curr_window + # } + # } return(curr_window) }, mc.cores = ncores) @@ -159,6 +205,78 @@ get_rolling_windows <- function(edge.list, return(dplyr::tibble("time" = starting_times, "network" = windows)) } +conditional_makeweighted <- function(x, select_cols, out_format){ + if(grepl('weight', out_format)){ + x %>% multi2weight(select_cols) -> x + } + return(x) +} + +#' @rdname get_rolling_windows +#' @export +get_rolling_windows.tbl_graph <- function(edge.list, + window_size, + step_size = NULL, + start_time = NULL, + end_time = NULL, + out_format = 'edgelist', + flush = "earliest", + select_cols = NULL, + as_date = NULL, + ncores = NULL, + ...) { + edge.list %>% tidygraph::activate('edges') %>% dplyr::as_tibble() %>% + get_rolling_windows(window_size, step_size, + start_time, end_time, out_format, + flush, select_cols, as_date, ncores, + nodes_tbl = edge.list %>% + tidygraph::activate('nodes') %>% + dplyr::as_tibble() %>% + dplyr::mutate(node_key = as.character(1:length(.data$name))), ...) + +} + +#' @rdname get_rolling_windows +#' @export +get_rolling_windows.igraph <- function(edge.list, + window_size, + step_size = NULL, + start_time = NULL, + end_time = NULL, + out_format = 'edgelist', + flush = "earliest", + select_cols = NULL, + as_date = NULL, + ncores = NULL, + ...) { + edge.list %>% tidygraph::as_tbl_graph() %>% + get_rolling_windows(edge.list, window_size, step_size, + start_time, end_time, out_format, + flush, select_cols, as_date, ncores, + tbl_graph = edge.list %>% tidygraph::as_tbl_graph(), ...) + +} + +#' @rdname get_rolling_windows +#' @export +get_rolling_windows.default <- function(edge.list, + window_size, + step_size = NULL, + start_time = NULL, + end_time = NULL, + out_format = 'edgelist', + flush = "earliest", + select_cols = NULL, + as_date = NULL, + ncores = NULL, + ...) { + edge.list %>% dplyr::as_tibble() %>% + get_rolling_windows(edge.list, window_size, step_size, + start_time, end_time, out_format, + flush, select_cols, as_date, ncores,...) + +} + #' Filter edgelist for a time slice #' #' @inheritParams get_adjacency diff --git a/man/get_adjacency.Rd b/man/get_adjacency.Rd index 608e635..8fb12d3 100644 --- a/man/get_adjacency.Rd +++ b/man/get_adjacency.Rd @@ -3,6 +3,7 @@ \name{get_adjacency} \alias{get_adjacency} \alias{get_adjacency.tbl} +\alias{get_adjacency.tbl_graph} \alias{get_adjacency.data.frame} \alias{get_adjacency.matrix} \alias{get_adjacency.dgTMatrix} @@ -40,6 +41,20 @@ get_adjacency( ... ) +\method{get_adjacency}{tbl_graph}( + x, + select_cols = NULL, + multiedge = NULL, + aggr_expression = NULL, + nodes = NULL, + sparse = TRUE, + drop_names = FALSE, + directed = NULL, + selfloops = NULL, + edgelist = NULL, + ... +) + \method{get_adjacency}{data.frame}( x, select_cols = NULL, diff --git a/man/get_rolling_windows.Rd b/man/get_rolling_windows.Rd index bb57381..1ccf66c 100644 --- a/man/get_rolling_windows.Rd +++ b/man/get_rolling_windows.Rd @@ -2,6 +2,10 @@ % Please edit documentation in R/temporal_nets.R \name{get_rolling_windows} \alias{get_rolling_windows} +\alias{get_rolling_windows.tbl} +\alias{get_rolling_windows.tbl_graph} +\alias{get_rolling_windows.igraph} +\alias{get_rolling_windows.default} \title{Builds rolling multiedge time-window networks from the edge_list.} \usage{ get_rolling_windows( @@ -17,10 +21,68 @@ get_rolling_windows( ncores = NULL, ... ) + +\method{get_rolling_windows}{tbl}( + edge.list, + window_size, + step_size = NULL, + start_time = NULL, + end_time = NULL, + out_format = "edgelist", + flush = "earliest", + select_cols = NULL, + as_date = NULL, + ncores = NULL, + nodes_tbl = NULL, + ... +) + +\method{get_rolling_windows}{tbl_graph}( + edge.list, + window_size, + step_size = NULL, + start_time = NULL, + end_time = NULL, + out_format = "edgelist", + flush = "earliest", + select_cols = NULL, + as_date = NULL, + ncores = NULL, + ... +) + +\method{get_rolling_windows}{igraph}( + edge.list, + window_size, + step_size = NULL, + start_time = NULL, + end_time = NULL, + out_format = "edgelist", + flush = "earliest", + select_cols = NULL, + as_date = NULL, + ncores = NULL, + ... +) + +\method{get_rolling_windows}{default}( + edge.list, + window_size, + step_size = NULL, + start_time = NULL, + end_time = NULL, + out_format = "edgelist", + flush = "earliest", + select_cols = NULL, + as_date = NULL, + ncores = NULL, + ... +) } \arguments{ -\item{edge.list}{A data-frame with at least 3 columns: "source", "target", -"timestamp", where "from" and "to" are vertex identifiers, and "time" is a +\item{edge.list}{A data-frame, tibble, with at least 3 columns: "source", +"target", "timestamp", or a graph object that can be coerced to such table, +where "from" and "to" are vertex identifiers, and "time" is a Unix-timestamp when the edge occurred. Alternatively, the user can select which column is which passing `select_cols`} @@ -37,9 +99,9 @@ starting at this timestamp. Else, they are started at min(edgelist$time).} they exceed this value. Else, this value defaults to max(edgelist$time).} \item{out_format}{character vector specifying the format of the output: -'edgelist' for edgelists, 'adjacency' for adjacency matrices, `igraph` for -igraph graphs. Defaults to 'edgelist'. In the case of igraph graphs, adding -`igraph_weighted` allows to generated weighted graphs.} +'edgelist' for edgelists, 'adjacency' for adjacency matrices, 'tbl_graph' for `tbl_graph`s, 'igraph' for +`igraph` graphs. Defaults to 'edgelist'. In the case of igraph and tbl_graph graphs, adding +'_weighted' (as in 'igraph_weighted') allows to generate weighted graphs.} \item{flush}{(optional) If "earliest" or unset, the first time-window starts at `start_time`, possibly excluding an incomplete time-window at @@ -60,6 +122,10 @@ formats, or unix seconds.} \item{...}{additional parameters passed to internal constructors. E.g., to `get_adjacency`.} + +\item{nodes_tbl}{(optional) tibble containing node attributes to be added to +tbl_graph objects when tbl_graph output is chosen. The tibble should have +a node_key column matching node names used in the edge.list.} } \value{ A tibble with two columns "network" and "time", where "network" is