##' Extract all possible paths from one variable to another connected component
##' in a latent variable model. In an estimated model the effect size is
##' decomposed into direct, indirect and total effects including approximate
##' standard errors.
##'
##' @title Extract pathways in model graph
##' @export
##' @aliases path effects path.lvm effects.lvmfit
##' totaleffects
##' @seealso \code{children}, \code{parents}
##' @return If \code{object} is of class \code{lvmfit} a list with the following
##' elements is returned \item{idx}{ A list where each element defines a
##' possible pathway via a integer vector indicating the index of the visited
##' nodes. } \item{V }{ A List of covariance matrices for each path. }
##' \item{coef }{A list of parameters estimates for each path} \item{path }{A
##' list where each element defines a possible pathway via a character vector
##' naming the visited nodes in order. } \item{edges }{Description of 'comp2'}
##'
##' If \code{object} is of class \code{lvm} only the \code{path} element will be
##' returned.
##'
##' The \code{effects} method returns an object of class \code{effects}.
##' @note For a \code{lvmfit}-object the parameters estimates and their
##' corresponding covariance matrix are also returned. The
##' \code{effects}-function additionally calculates the total and indirect
##' effects with approximate standard errors
##' @author Klaus K. Holst
##' @keywords methods models graphs
##' @examples
##'
##' m <- lvm(c(y1,y2,y3)~eta)
##' regression(m) <- y2~x1
##' latent(m) <- ~eta
##' regression(m) <- eta~x1+x2
##' d <- sim(m,500)
##' e <- estimate(m,d)
##'
##' path(Model(e),y2~x1)
##' parents(Model(e), ~y2)
##' children(Model(e), ~x2)
##' children(Model(e), ~x2+eta)
##' effects(e,y2~x1)
##' ## All simple paths (undirected)
##' path(m,y1~x1,all=TRUE)
##'
##' @usage
##' \method{path}{lvm} (object, to = NULL, from, all=FALSE, ...)
##' \method{effects}{lvmfit} (object, to, from, ...)
##' @param object Model object (\code{lvm})
##' @param to Outcome variable (string). Alternatively a formula specifying
##' response and predictor in which case the argument \code{from} is ignored.
##' @param from Response variable (string), not necessarily directly affected by
##' \code{to}.
##' @param all If TRUE all simple paths (in undirected graph) is returned
##' on/off.
##' @param \dots Additional arguments to be passed to the low level functions
##' @export
path <- function(object,...) UseMethod("path")
##' @export
path.lvmfit <- function(object,to=NULL,from,...) {
mypath <- pathM(Model(object)$M,to,from,...)
cc <- coef(object,type=9,labels=FALSE) ## All parameters (fixed and variable)
cc0 <- coef(object,type=2) ## Estimated parameters
i1 <- na.omit(match(rownames(cc),rownames(cc0)))
idx.cc0 <- which(rownames(cc)%in%rownames(cc0)); ## Position of estimated parameters among all parameters
S <- matrix(0,nrow(cc),nrow(cc)); rownames(S) <- colnames(S) <- rownames(cc)
V <- object$vcov
S[idx.cc0,idx.cc0] <- V[i1,i1] ## "Covariance matrix" of all parameters
idx <- list()
coefs <- list()
V <- list()
for (i in seq_along(mypath)) {
xx <- mypath[[i]]
ii <- c()
for (j in seq_len(length(xx)-1)) {
st <- paste0(xx[j+1], lava.options()$symbol[1], xx[j])
ii <- c(ii, match(st,rownames(cc)))
}
idx <- c(idx, list(ii))
V <- c(V, list(S[ii,ii]))
coefs <- c(coefs, list(cc[ii]))
}
edges <- list()
for (i in seq_along(mypath)) {
p0 <- mypath[[i]]
ee <- c()
for (i in seq_len(length(p0)-1)) {
ee <- c(ee, paste(p0[i],p0[i+1],sep="~"))
}
edges <- c(edges, list(ee))
}
res <- list(idx=idx,V=V,coef=coefs, path=mypath, edges=edges)
return(res)
}
##' @export
path.lvm <- function(object,to=NULL,from,all=FALSE,...) {
pathM(object$M,to=to,from=from,all=all,...)
}
##' @export
path.graphNEL <- function(object,to,from,...) {
if (inherits(to,"formula")) {
fvar <- extractvar(to)
if (length(fvar$x)==1 & length(fvar$y)==1)
return(path(object,to=fvar$y,from=fvar$x))
res <- list()
for (y in fvar$y) {
for (x in fvar$x) {
cat("x=",x, " y=",y, "\n")
res <- c(res, list(path(object,to=y,from=x)))
}
}
return(res)
}
ff <- function(g,from=1,to=NULL,res=list()) {
M <- graph::edgeMatrix(g)
i1 <- which(M[1,]==from)
for (i in i1) {
e <- M[,i]; newto <- e[2];
if (is.null(to) || M[2,i]==to) {
res <- c(res, list(M[,i]))
}
newpath <- ff(g,from=newto,to=to,list())
if (length(newpath)>0)
for (j in seq_along(newpath)) {
if (is.null(to) || (tail(newpath[[j]],1)==to))
res <- c(res, list(c(M[,i],newpath[[j]][-1])))
}
}
return(res)
}
idxfrom <- ifelse(is.numeric(from),from,which(from==graph::nodes(object)))
##M <- as(object,"matrix")
##reachable <- acc(M,graph::nodes(object)[idxfrom])
reachable <- graph::acc(object,graph::nodes(object)[idxfrom])[[1]]
if (is.null(to)) {
idxto <- reachable
} else {
idxto <- ifelse(is.numeric(to),to,which(to==graph::nodes(object)))
}
if (!(graph::nodes(object)[idxto] %in% names(reachable)))
## return(structure(list(),to=to[1],from=from[1]))
return(NULL)
## stop("No directional relationship between variables")
mypaths <- ff(object,idxfrom,idxto)
res <- list()
for (i in seq_along(mypaths)) {
res <- c(res, list(graph::nodes(object)[mypaths[[i]]]))
}
return(res)
}
pathM <- function(M,to,from,all=FALSE,...) {
nn <- colnames(M)
if (inherits(to,"formula")) {
fvar <- extractvar(to)
if (length(fvar$x)==1 & length(fvar$y)==1)
return(pathM(M,to=fvar$y,from=fvar$x,all=all))
res <- list()
for (y in fvar$y) {
for (x in fvar$x) {
cat("x=",x, " y=",y, "\n")
res <- c(res, list(pathM(M,to=y,from=x,all=all)))
}
}
return(res)
}
if (all) { ## Get all simple paths
res <- simplePaths(to,from,from,M,list())
return(res)
}
ff <- function(g,from=1,to=NULL,res=list()) {
i1 <- which(M[from,]==1)
for (i in i1) {
## e <- M[,i]; newto <- e[2];
if (is.null(to) || i==to) {
res <- c(res, list(c(from,i)))
}
newpath <- ff(g,from=i,to=to,list())
if (length(newpath)>0)
for (j in seq_along(newpath)) {
if (is.null(to) || (tail(newpath[[j]],1)==to))
res <- c(res, list(c(c(from,i),newpath[[j]][-1])))
}
}
return(res)
}
idxfrom <- ifelse(is.numeric(from),from,which(from==nn))
reachable <- acc(M,nn[idxfrom])
if (is.null(to)) {
idxto <- reachable
} else {
idxto <- ifelse(is.numeric(to),to,which(to==nn))
}
if (!(nn[idxto] %in% reachable))
return(NULL)
## stop("No directional relationship between variables")
mypaths <- ff(M,idxfrom,idxto)
res <- list()
for (i in seq_along(mypaths)) {
res <- c(res, list(nn[mypaths[[i]]]))
}
return(res)
}
## Find all simple paths (no cycles) in an undirected graph
simplePaths <- function(target,currentpath,visited,adjmat,allpaths) {
lastnode <- currentpath[length(currentpath)]
A <- (adjmat+t(adjmat))>0
if (lastnode==target) {
allpaths <- c(allpaths,list(currentpath))
} else {
for (neighbour in rownames(adjmat)[which(A[,lastnode])]) {
if (!(neighbour%in%visited)) {
currentpath <- c(currentpath,neighbour)
visited <- c(visited,neighbour)
allpaths <- simplePaths(target,currentpath,visited,adjmat,allpaths)
visited <- setdiff(visited,neighbour)
currentpath <- currentpath[-length(currentpath)]
}
}
}
return(allpaths)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.