darwin-eu-dev / PaRe

PaRe (Package Reviewer) is the successor of the DependencyReviewer package.
https://darwin-eu-dev.github.io/PaRe/
3 stars 3 forks source link

Option to subset package overview plot #29

Closed edward-burn closed 1 year ago

edward-burn commented 1 year ago

To help aid refactoring, I think it would be nice if there was the ability to

mvankessel-EMC commented 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'),
    "}"
  )
)

image

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)

image

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"

image

mvankessel-EMC commented 1 year ago

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. image Fig 1. IncidencePrevalence

image Fig 2. Sub path for estimatePrevalence

Currently testing it CohortMethod where it behaves unexpectantly, but it seems to work for IncidnecePrevalence.

edward-burn commented 1 year ago

@mvankessel-EMC awesome, this looks great!

mvankessel-EMC commented 1 year ago

@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)

image

PaRe::getFunctionDiagram(repo, "funsUsedInLine")

image

Could you try it out, report back, and let me know what you think?

mvankessel-EMC commented 1 year ago

Included in latest version 0.1.7 294bd2278ee1906d6a0768dcc545e855986ce79c