Closed edward-burn closed 1 year ago
@edward-burn I've been messing around with this a little. I landed on the following implementation for now:
Given the following graph
nodes <- c(
"A -> B",
"A -> C",
"A -> D",
"A -> E",
"D -> F",
"B -> G",
"C -> F",
"F -> H"
)
DiagrammeR::grViz(
paste0(
"digraph {graph [layout = dot, rankdir = LR]",
paste0(nodes, collapse = '\n'),
"}"
)
)
Lets say we want to see the full path of node C.
We need to be able to go forward and backwards from that node.
forward <- function(c) {
unlist(lapply(c, function(node) {
start <- nodes[startsWith(nodes, node)]
substr(start, nchar(start), nchar(start))
}))
}
forward("C")
[1] "F"
backward <- function(c) {
unlist(lapply(c, function(node) {
start <- nodes[endsWith(nodes, c)]
substr(start, 1, 1)
}))
}
backward("C")
[1] "A"
Multiple nodes may be connected to a node, therefore we need to apply our implementation for each incoming or outgoing node.
From this point on we need to go forward, but recursively
recForward <- function(c) {
f <- forward(c)
if (length(f) > 0) {
f <- unlist(lapply(f, recFor))
return(paste(c, "->", f))
} else {
return(c)
}
}
recForward("C")
[1] "C -> F -> H"
And recursively backwards
recBackward <- function(c) {
f <- backward(c)
if (length(f) > 0) {
f <- unlist(lapply(f, recBack))
return(paste(f, "->", c))
} else {
return(c)
}
}
recBackward("C")
[1] "A -> C"
Then we use those in a function to plot our diagram:
funDiagram <- function(funName, nodes) {
subFor <- paste0(recForward(funName), collapse = "\n")
subBack <- paste0(recBackward(funName), collapse = "\n")
DiagrammeR::grViz(
paste0(
"digraph {graph [layout = dot, rankdir = LR]",
paste0(c(subFor, subBack), collapse = "\n"),
"}"))
}
funDiagram("C", nodes)
The only issue with this implementation is that it will plot duplicate paths, i.e. if we look for all paths of A.
funDiagram("A", nodes)
Because the following paths are produced:
# Forward from A
"A -> B -> G"
"A -> C -> F -> H"
"A -> D -> F -> H"
"A -> E"
# Backward from A
"A"
I've pushed an update to the dev branch for this. It now also handles looping paths, with a maxIter
parameter supplied by the user.
Fig 1. IncidencePrevalence
Fig 2. Sub path for estimatePrevalence
Currently testing it CohortMethod where it behaves unexpectantly, but it seems to work for IncidnecePrevalence.
@mvankessel-EMC awesome, this looks great!
@edward-burn I just pushed an update to dev
. I ended up using igraph
to compute the incoming and outgoing paths from a specific node.
repo <- PaRe::Repository$new("../PaRe/")
PaRe::pkgDiagram(repo)
PaRe::getFunctionDiagram(repo, "funsUsedInLine")
Could you try it out, report back, and let me know what you think?
Included in latest version 0.1.7 294bd2278ee1906d6a0768dcc545e855986ce79c
To help aid refactoring, I think it would be nice if there was the ability to