R/network_utils.R

Defines functions is_connected_adj components_adj reciprocated symmetrize adj_to_dyadlist

Documented in adj_to_dyadlist components_adj is_connected_adj reciprocated symmetrize

#' Transform adjacency matrix to dyad-list
#' 
#' Transforms an adjacency matrix of dimension \code{n} times \code{n} to a \code{choose(n, 2)} times \code{4} dyad-list.
#' 
#' @param x numeric or integer matrix
#' @return dyad-list
#' @details The first column of the returned object contains identifiers for the rows of the adjacency matrix \code{x}, \code{i}, the second column contains identifiers for the columns, \code{j}, the third column contains the tie from \code{i} to \code{j}, and the fourth column contains the tie from \code{j} to \code{i}. 
#' 
#' The function will automatically determine whether \code{x} is symmetric or not. If \code{x} is symmetric, the returned dyadlist will consists of the first three columns only.
#' 
#' Notice that a \code{matrix} class in \code{R} can hold only a single data type. So, if the adjacency matrix contains \code{character}-types, the resulting dyadlist will be of type \code{character} as well. On the other hand, if the adjacency matrix contains \code{logical} types, the function will return an \code{integer} type dyad-list.
#' @examples 
#' # asymmetric case
#' A = matrix(runif(25), nrow = 5)
#' adj_to_dyadlist(A)
#' 
#' # symmetric case
#' A_sym = btoolbox::symmetrize(A, upper_to_lower = TRUE)
#' adj_to_dyadlist(A_sym)
#' @export
adj_to_dyadlist = function(x) {
    
    # check whether x is a matrix
    if (!is.matrix(x))
        stop("x has to be a matrix object")
    
    if (nrow(x) != ncol(x))
        stop("x is not a square matrix")
    
    sym = isSymmetric(x) 
    is_lg = typeof(x) == "logical"
    if (is_lg) 
        x = apply(x, 2L, as.integer)
    
    # transform to dyadlist
    res = .adj_to_dyad_C(x, sym)
    
    colnames(res) = if (sym) {
        c("row", "col", "tie")
    } else {
        c("row", "col", "row_to_col", "col_to_row")
    }
    
    return(res)
    
}

#' Symmetrize square matrix
#' 
#' @param x a square matrix
#' @param upper_to_lower if TRUE, copies the upper triangle into
#'        the lower triangle; copies the lower triangle into the
#'        upper triangle otherwise. Defaults to TRUE.
#' @return returns symmetrized version of \code{x}
#' @examples 
#' A = matrix(rnorm(9), nrow = 3)
#' A_sym = symmetrize(A, upper_to_lower = FALSE)
#' isSymmetric(A_sym)
#' @export
symmetrize = function(x, upper_to_lower = TRUE) {
    
    if (!is.matrix(x))
        stop("x has to be a matrix")
    
    if (ncol(x) != nrow(x))
        stop("x has to be a sqaure matrix")
    
    .symmetrize_square_matC(x, upper_to_lower)
    
}


#' Tie reciprocity
#' 
#' Creates a vector of the same length as an edge list indicating whether each tie is reciprocated
#' 
#' @param x numeric or integer matrix of two columns (can also be a data.table/data.frame)
#' @return length \code{nrow(x)} vector of zeros (not reciprocated) and ones (reciprocated)
#' @export
reciprocated = function(x)
{
    
    if (anyDuplicated(x))
        stop("x has duplicated rows")

    if (NCOL(x) != 2)
        stop("x must have only two columns")
    
    if (is.list(x) && "data.frame" %in% class(x)) {
        
        if (!all(sapply(x, class) %in% c("numeric", "integer")))
            stop("x must be either numeric or integer")
        
        x = as.matrix(x)
        
    } else if (is.array(x)) {
        
        if (!is.numeric(x))
            stop("x must be numeric or integer")
        
    } else {
        
        stop("only arrays and data.fames are supported")
        
    }
    
    return(.is_reciproc(x))
    
}

#' Determine components of graph using adjacency matrix
#' 
#' Determines the components of a graph from a (possibly weighted) adjacency matrix using depth-first search
#' 
#' @param x, a adjacency matrix
#' @returns an integer vector with component membership of each node
#' @details indices of the component vector starts from 1 and not 0.
#' @export
components_adj = function(x)
{

    if (!requireNamespace("Matrix", quietly = TRUE))
        stop("need Matrix package to be installed")
    
    if (!methods::is(x, "sparseMatrix"))
        x = Matrix::Matrix(x, sparse = TRUE)
    
    return(components_sp(x))

}

#' Check whether graph is connected using the associated adjacency matrix
#' 
#' @param x a adjacency matrix
#' @returns \code{TRUE} if graph is connected, \code{FALSE} otherwise
#' @export
is_connected_adj = function(x)
{
    
    if (!requireNamespace("Matrix", quietly = TRUE))
        stop("need Matrix package to be installed")
    
    if (!methods::is(x, "sparseMatrix"))
        x = Matrix::Matrix(x, sparse = TRUE)
    
    return(is_connected_sp(x))
    
}
baruuum/btoolbox documentation built on Aug. 17, 2020, 1:29 a.m.