diff --git a/gui/js/dagitty.js b/gui/js/dagitty.js index ab7d782..4e9e7a4 100644 --- a/gui/js/dagitty.js +++ b/gui/js/dagitty.js @@ -4541,7 +4541,7 @@ var GraphTransformer = { } }) }) - + // Delete all vertices that have not been marked for retention if( opts.direct ){ _.each( intermediates_after_source, function(v){ retain[v.id] = true } ) @@ -4551,7 +4551,7 @@ var GraphTransformer = { g_chain.deleteVertex(v) } } ) - + // Restore original edge types var edges_to_replace = [] _.each( g_chain.edges, function(e){ @@ -4567,7 +4567,8 @@ var GraphTransformer = { g_chain.addEdge( e.v2.id, e.v1.id, Graph.Edgetype.Directed ) } } ) - + + // Replace dummy nodes from canonical graph with original nodess var Lids = _.pluck(g_canon.L,"id"), Sids = _.pluck(g_canon.S,"id") L=[], S=[] @@ -4582,17 +4583,25 @@ var GraphTransformer = { // For direct effects, add causal paths between mediators if( opts.direct ){ - var gind = this.inducedSubgraph( g, _.intersection( g.descendantsOf( g.getSources() ), + _.each( Z, Graph.Vertex.markAsVisited ) + var gind = this.inducedSubgraph( g, _.intersection( g.descendantsOf( g.getSources(), preserve_previous_visited_information ), g.ancestorsOf( g.getTargets() ) ) ) _.each( gind.edges, function(e){ if( e.directed == Graph.Edgetype.Directed && !( (gind.isSource(e.v1)||gind.isTarget(e.v1)) && (gind.isSource(e.v2)||gind.isTarget(e.v2))) ){ + if( !g_chain.getVertex( e.v1.id ) ){ + g_chain.addVertex( e.v1.id ) + } + if( !g_chain.getVertex( e.v2.id ) ){ + g_chain.addVertex( e.v2.id ) + } g_chain.addEdge( e.v1.id, e.v2.id, Graph.Edgetype.Directed ) } }) } g = GraphTransformer.decanonicalize( g_chain, L, S ) g.setType( in_type ) + return g }, // end of activeBiasGraph diff --git a/jslib/graph/GraphTransformer.js b/jslib/graph/GraphTransformer.js index cbfba63..0c85144 100644 --- a/jslib/graph/GraphTransformer.js +++ b/jslib/graph/GraphTransformer.js @@ -764,7 +764,7 @@ var GraphTransformer = { } }) }) - + // Delete all vertices that have not been marked for retention if( opts.direct ){ _.each( intermediates_after_source, function(v){ retain[v.id] = true } ) @@ -774,7 +774,7 @@ var GraphTransformer = { g_chain.deleteVertex(v) } } ) - + // Restore original edge types var edges_to_replace = [] _.each( g_chain.edges, function(e){ @@ -790,7 +790,8 @@ var GraphTransformer = { g_chain.addEdge( e.v2.id, e.v1.id, Graph.Edgetype.Directed ) } } ) - + + // Replace dummy nodes from canonical graph with original nodess var Lids = _.pluck(g_canon.L,"id"), Sids = _.pluck(g_canon.S,"id") L=[], S=[] @@ -805,17 +806,25 @@ var GraphTransformer = { // For direct effects, add causal paths between mediators if( opts.direct ){ - var gind = this.inducedSubgraph( g, _.intersection( g.descendantsOf( g.getSources() ), + _.each( Z, Graph.Vertex.markAsVisited ) + var gind = this.inducedSubgraph( g, _.intersection( g.descendantsOf( g.getSources(), preserve_previous_visited_information ), g.ancestorsOf( g.getTargets() ) ) ) _.each( gind.edges, function(e){ if( e.directed == Graph.Edgetype.Directed && !( (gind.isSource(e.v1)||gind.isTarget(e.v1)) && (gind.isSource(e.v2)||gind.isTarget(e.v2))) ){ + if( !g_chain.getVertex( e.v1.id ) ){ + g_chain.addVertex( e.v1.id ) + } + if( !g_chain.getVertex( e.v2.id ) ){ + g_chain.addVertex( e.v2.id ) + } g_chain.addEdge( e.v1.id, e.v2.id, Graph.Edgetype.Directed ) } }) } g = GraphTransformer.decanonicalize( g_chain, L, S ) g.setType( in_type ) + return g }, // end of activeBiasGraph diff --git a/r/R/dagitty.r b/r/R/dagitty.r index 6144191..3aea164 100644 --- a/r/R/dagitty.r +++ b/r/R/dagitty.r @@ -411,6 +411,9 @@ impliedCovarianceMatrix <- function( x, b.default=NULL, b.lower=-.6, b.upper=.6, #' #' @param x the input graph, of any type. #' @param v name(s) of variable(s). +#' @param proper logical. By default (\code{proper=FALSE}), the \code{descendants} or \code{ancestors} +#' of a variable include the variable itself. For (\code{proper=TRUE}), the variable itself +#' is not included. #' #' \code{descendants(x,v)} retrieves variables that are are reachable from \code{v} via #' a directed path. @@ -435,7 +438,10 @@ impliedCovarianceMatrix <- function( x, b.default=NULL, b.lower=-.6, b.upper=.6, #' #' @examples #' g <- dagitty("graph{ a <-> x -> b ; c -- x <- d }") +#' # Includes "x" #' descendants(g,"x") +#' # Does not include "x" +#' descendants(g,"x",TRUE) #' parents(g,"x") #' spouses(g,"x") #' @@ -443,14 +449,24 @@ NULL #' @rdname AncestralRelations #' @export -descendants <- function( x, v ){ - .kins( x, v, "descendants" ) +descendants <- function( x, v, proper=FALSE ){ + r <- .kins( x, v, "descendants" ) + if( proper ){ + setdiff( r, v ) + } else { + r + } } #' @rdname AncestralRelations #' @export -ancestors <- function( x, v ){ - .kins( x, v, "ancestors" ) +ancestors <- function( x, v, proper=FALSE ){ + r <- .kins( x, v, "ancestors" ) + if( proper ){ + setdiff( r, v ) + } else { + r + } } #' @rdname AncestralRelations diff --git a/r/inst/js/dagitty-alg.js b/r/inst/js/dagitty-alg.js index c21f753..a268bc2 100644 --- a/r/inst/js/dagitty-alg.js +++ b/r/inst/js/dagitty-alg.js @@ -4536,7 +4536,7 @@ var GraphTransformer = { } }) }) - + // Delete all vertices that have not been marked for retention if( opts.direct ){ _.each( intermediates_after_source, function(v){ retain[v.id] = true } ) @@ -4546,7 +4546,7 @@ var GraphTransformer = { g_chain.deleteVertex(v) } } ) - + // Restore original edge types var edges_to_replace = [] _.each( g_chain.edges, function(e){ @@ -4562,7 +4562,8 @@ var GraphTransformer = { g_chain.addEdge( e.v2.id, e.v1.id, Graph.Edgetype.Directed ) } } ) - + + // Replace dummy nodes from canonical graph with original nodess var Lids = _.pluck(g_canon.L,"id"), Sids = _.pluck(g_canon.S,"id") L=[], S=[] @@ -4577,17 +4578,25 @@ var GraphTransformer = { // For direct effects, add causal paths between mediators if( opts.direct ){ - var gind = this.inducedSubgraph( g, _.intersection( g.descendantsOf( g.getSources() ), + _.each( Z, Graph.Vertex.markAsVisited ) + var gind = this.inducedSubgraph( g, _.intersection( g.descendantsOf( g.getSources(), preserve_previous_visited_information ), g.ancestorsOf( g.getTargets() ) ) ) _.each( gind.edges, function(e){ if( e.directed == Graph.Edgetype.Directed && !( (gind.isSource(e.v1)||gind.isTarget(e.v1)) && (gind.isSource(e.v2)||gind.isTarget(e.v2))) ){ + if( !g_chain.getVertex( e.v1.id ) ){ + g_chain.addVertex( e.v1.id ) + } + if( !g_chain.getVertex( e.v2.id ) ){ + g_chain.addVertex( e.v2.id ) + } g_chain.addEdge( e.v1.id, e.v2.id, Graph.Edgetype.Directed ) } }) } g = GraphTransformer.decanonicalize( g_chain, L, S ) g.setType( in_type ) + return g }, // end of activeBiasGraph diff --git a/r/man/AncestralRelations.Rd b/r/man/AncestralRelations.Rd index 96a2127..f2fb048 100644 --- a/r/man/AncestralRelations.Rd +++ b/r/man/AncestralRelations.Rd @@ -12,9 +12,9 @@ \alias{markovBlanket} \title{Ancestral Relations} \usage{ -descendants(x, v) +descendants(x, v, proper = FALSE) -ancestors(x, v) +ancestors(x, v, proper = FALSE) children(x, v) @@ -31,7 +31,11 @@ markovBlanket(x, v) \arguments{ \item{x}{the input graph, of any type.} -\item{v}{name(s) of variable(s). +\item{v}{name(s) of variable(s).} + +\item{proper}{logical. By default (\code{proper=FALSE}), the \code{descendants} or \code{ancestors} +of a variable include the variable itself. For (\code{proper=TRUE}), the variable itself +is not included. \code{descendants(x,v)} retrieves variables that are are reachable from \code{v} via a directed path. @@ -58,7 +62,10 @@ ancestral relationship to the input variable \code{v}. } \examples{ g <- dagitty("graph{ a <-> x -> b ; c -- x <- d }") +# Includes "x" descendants(g,"x") +# Does not include "x" +descendants(g,"x",TRUE) parents(g,"x") spouses(g,"x") diff --git a/r/tests/testthat/testBasics.R b/r/tests/testthat/testBasics.R index d98e82e..3890624 100644 --- a/r/tests/testthat/testBasics.R +++ b/r/tests/testthat/testBasics.R @@ -86,3 +86,9 @@ test_that("children and parents of nothing", { expect_equal( length(parents("a->b",c())), 0 ) } ) +test_that("proper and non-proper ancestors ",{ + expect_equal( "x" %in% ancestors(dagitty("graph{a <-> x -> b ; c -- x <- d}"), "x"), TRUE ) + expect_equal( "x" %in% ancestors(dagitty("graph{a <-> x -> b ; c -- x <- d}"), "x",TRUE), FALSE ) + expect_equal( "x" %in% descendants(dagitty("graph{a <-> x -> b ; c -- x <- d}"), "x"), TRUE ) + expect_equal( "x" %in% descendants(dagitty("graph{a <-> x -> b ; c -- x <- d}"), "x",TRUE), FALSE ) +} ) diff --git a/test/test/biasing-paths.js b/test/test/biasing-paths.js index 0e56f0e..fac3c6a 100644 --- a/test/test/biasing-paths.js +++ b/test/test/biasing-paths.js @@ -66,11 +66,17 @@ assert.equal((function(){ return $es(GraphTransformer.activeBiasGraph(g)) })(), "" ) - assert.equal( GraphTransformer.activeBiasGraph( new Graph("x->m->y z->{x y}"), { X:["x"] , Y:["y"] } ).edges.length, 2 ) assert.equal( GraphTransformer.activeBiasGraph( new Graph("x->m->y"), {direct : true, X:["x"], Y:["y"] } ).edges.length, 2 ) +assert.equal( GraphTransformer.activeBiasGraph( new Graph("x->m->y"), {direct : true, X:["x"], Y:["y"] } ).edges.length, 2 ) + +assert.equal( GraphTransformer.activeBiasGraph( new Graph("x->m->y"), {direct : true, X:["x"], Y:["y"] } ).edges.length, 2 ) +assert.equal( GraphTransformer.activeBiasGraph( new Graph("x->{m[a]}->y"), {direct : true, X:["x"], Y:["y"] } ).edges.length, 0 ) +assert.equal( GraphTransformer.activeBiasGraph( new Graph("x->{{m[a]}->y}<-z"), {direct : true, X:["x"], Y:["y"] } ).edges.length, 3 ) +assert.equal( GraphTransformer.activeBiasGraph( new Graph("x->{{m[a]}->y}<-{z[a]}"), {direct : true, X:["x"], Y:["y"] } ).edges.length, 0 ) + });