1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
|
#' Comparing and testing network objects
#'
#' Compare or test network objects for (near) equality.
#'
#' Arguments \code{target} and \code{current} can be network objects of one of
#' the supported classes. They do not have to be of the same class though.
#'
#' The function does a series of comparisons between \code{target} and
#' \code{current}:
#'
#' \enumerate{
#' \item The network structure comparison is made based on adjacency matrices
#' (mind this when using for huge networks).
#'
#' \item Network/edge/vertex attributes are checked for presence in both
#' objects.
#'
#' \item Common network/edge/vertex attribures are checked for equality.
#' }
#' All the results are collected in a list of class \code{netcompare} with an
#' associated \code{print} method.
#'
#' If \code{test} is TRUE then instead of the detailed test results the function
#' returns TRUE or FALSE depending on some of the checks resulted positively.
#' Currently attribute checks are ignored, i.e. what is taken into account is:
#'
#' \enumerate{
#' \item Equivalence of adjacency matrices
#' \item Directed / undirected character of the network
#' \item Edge set size
#' \item Vertex set size
#' }
#'
#' @param target,current network objects, currently \code{network} and
#' \code{igraph} classes are supported
#'
#' @param test logical, whether to perform the test or return comparison data,
#' see Details
#'
#' @param \dots other arguments, currently ignored
#'
#' @return Depending on the value of \code{test} either an object of class
#' \code{netcompare} containing the results of all the tests (if
#' \code{test=FALSE}) or (if \code{test=TRUE}) a logical whether or not the
#' networks are (nearly) the same.
#'
#' @seealso \code{\link{all.equal}}, \code{\link{identical}}
#'
#' @export
#'
#' @examples
#'
#'netcompare( asIgraph(exNetwork), exNetwork)
#'netcompare( asIgraph(exNetwork), exNetwork, test=TRUE)
#'
netcompare <- function(target, current, test=FALSE, ...)
{
# trivial checks
rval <- list()
# class
rval$class <- c(target=class(target), current=class(current))
# number of vertices
rval$vcount <- c(target=igVcount(target), current=igVcount(current))
# number of edges
rval$ecount <- c( target=igEcount(target), current=igEcount(current))
# directedness
rval$directed <- c( target=igDirected(target), current=igDirected(current))
# compare adjacency matrices
rval$identical_am <- compareEdges(target, current)
# compare attributes
targeta <- dumpAttr(target)
currenta <- dumpAttr(current)
rval$network <- compareAttributes( targeta$network, currenta$network)
rval$vertex <- compareAttributes( targeta$vertex, currenta$vertex)
rval$edge <- compareAttributes( targeta$edge, currenta$edge)
rval <- structure(rval, class="netcompare")
if(test)
compareTest(rval)
else return(rval)
}
#' @export
print.netcompare <- function(x, ...)
{
cat("\n")
cat("Identical adjacency matrices:\n")
cat( paste(x$identical_am, collapse=", "), "\n", fill=TRUE, labels=" ")
cat("Network-level features:\n")
m <- do.call("rbind", lapply(x[c("vcount", "ecount", "directed")], format))
print(m, quote=FALSE)
cat("\n")
cat("Presence of network-level attributes\n")
print(x$network)
cat("\n")
cat("Presence of vertex-level attributes\n")
print(x$vertex)
cat("\n")
cat("Presence of edge-level attributes\n")
print(x$edge)
}
# comparing and testing for (near) equality of networks
#
# Result of comparison:
#
# 1. computed network-level comparisons
#
# 2. built-in network-level comparisons
#
# 3. attributes
#
# 3a. attribute presence
#
# 3b. identical common attributes
#
# NOTE: Makes use of non-exported generic functions
compareTest <- function(object)
{
stopifnot(inherits(object, "netcompare"))
rval <- logical()
rval["adjacency"] <- object$identical_am
rval["vcount"] <- object$vcount[1] == object$vcount[2]
rval["ecount"] <- object$ecount[1] == object$ecount[2]
rval["directed"] <- object$directed[1] == object$directed[2]
all(rval)
}
compareEdges <- function(target, current, use.names=FALSE)
{
op <- igraph::igraph_options(sparsematrices = FALSE)
on.exit(igraph::igraph_options(op))
tr <- try(utils::getS3method("as.matrix", class=class(target)), silent=TRUE)
if(inherits(tr, "try-error"))
stop("cannot find 'as.matrix' method for class ", dQuote(class(target)))
tr <- try(utils::getS3method("as.matrix", class=class(current)), silent=TRUE)
if(inherits(tr, "try-error"))
stop("cannot find 'as.matrix' method for class ", dQuote(class(current)))
mtar <- as.matrix(target, "adjacency")
mcur <- as.matrix(current, "adjacency")
# compare matrices (no dimnames)
if(use.names)
all.equal(mtar, mcur)
else all.equal( structure(mtar, dimnames=NULL), structure(mcur,
dimnames=NULL) )
}
# compare common components of a list (by name)
# return a list of all.equal results
compareAlist <- function(target, current)
{
# common components
nams <- intersect(names(target), names(current))
if( length(nams) == 0 )
return(as.character(NA))
rval <- lapply(nams, function(n) all.equal( target[[n]], current[[n]]) )
names(rval) <- nams
rval
}
# Compare lists of attributes (as returned by 'dumpAttr')
compareAttributes <- function(target, current)
{
rval <- list()
# compare number of attributes
rval$n <- c(target=length(target), current=length(current))
# Check for attributes by name
pre <- list()
u <- union(names(target), names(current))
if(length(u) == 0L) {
pre <- matrix(NA, 0, 2, dimnames = list(NULL, c("target", "current")))
} else {
r <- t(sapply(u, function(a)
c( a %in% names(target),
a %in% names(current) )
))
pre <- c(pre, list(r))
pre <- do.call("rbind", pre)
dimnames(pre) <- list(rownames(pre), c("target", "current"))
}
rval$presence <- pre
rval$bycomp <- compareAlist(target, current)
structure(rval, class="netcomparea")
}
# Print method for the result of 'compareAttributes'
#' @export
print.netcomparea <- function(x, ...)
{
m <- do.call("rbind", lapply( x[c("n", "presence")], format))
print(m, quote=FALSE)
cat("Common attributes comparison (TRUE=identical)\n")
if( identical( x$bycomp, as.character(NA)) )
{
cat(" No common attributes\n")
} else
{
l <- sapply(x$bycomp, paste, collapse=", ")
for(i in seq(along=l))
cat(names(l)[i], ":", l[i], fill=TRUE, labels=" ")
}
}
|