Example `reactflow` of a `targets` Pipeline
Source:vignettes/example_targets.Rmd
example_targets.Rmd
The targets
R
package is a wonderful tool for reproducible data science workflows. To
visualize a targets pipeline, there is the default
tar_visnetwork()
function, which creates a network
visualization of the pipeline.
As this information is by definition a graph, reactflow
can be used to visualize and style the pipeline in a more interactive
way.
Note we won’t be covering the targets
package in this
vignette, the code to create the targets pipeline etc is included in
reactflow
in data-raw/example_targets.R
on
Github.
This reactflow
package provides two sample datasets,
targets_meta
(output of targets::tar_meta()
)
and targets_network
(output of
targets::tar_network()
).
To prepare the data for the reactflow
visualization, we
need to convert the targets_network
data to a format that
reactflow
can understand. That is, we need to convert the
data.frame to a list of nodes and a list of edges.
library(reactflow)
library(bsicons)
tar <- targets_network
# merge meta information to identify files
tar$vertices <- merge(tar$vertices, targets_meta[, c("name", "format")],
by.x = "name", by.y = "name")
icons <- list(
file = bs_icon("file-earmark-fill", size = 15),
stem = bs_icon("type", size = 15),
"function" = bs_icon("braces", size = 15)
)
nodes <- lapply(seq_len(nrow(tar$vertices)), \(i) {
el <- tar$vertices[i, ]
name <- el$name
icon <- icons[[ifelse(!is.na(el$format) & el$format == "file", "file", el$type)]]
time <- ifelse(el$status == "uptodate",
ifelse(is.na(el$seconds), "NA", paste0(el$seconds, "s")),
"NA")
html <- as.character(
htmltools::div(
style = "display: flex; flex-direction: row; align-items: center;",
class = el$status,
htmltools::div(icon, style = "margin-right: 10px;"),
htmltools::div(
htmltools::div(
style = "font-size: 1.1em; font-weight: bold; color: #999999",
name
),
htmltools::div(
style = "font-size: 0.8em; font-weight: 600; color: darkgray;",
paste0("Time: ", time)
)
)
)
)
list(
id = el$name,
type = "html",
data = list(html = html,
status = el$status, # for mini-map later on
handle = list(direction = "LR"))
)
})
edges <- apply(tar$edges, 1, \(x) {
list(
id = paste(x[["from"]], x[["to"]], sep = "_"),
source = x[["from"]],
target = x[["to"]]
)
})
Note we also need to set some general CSS to make the nodes look a bit nicer
.react-flow__node.react-flow__node-html {
padding: 10px;
border-radius: 15px;
border: 4px solid gray;
width: 150px;
font-size: 12px;
text-align: center;
background-color: var(--xy-node-background-color, var(--xy-node-background-color-default));
}
.react-flow__node.react-flow__node-html:has(.outdated) {
border-color: lightcoral;
color: lightcoral;
}
.react-flow__node.react-flow__node-html:has(.uptodate) {
border-color: mediumseagreen;
color: mediumseagreen;
}
/* style the handles */
.react-flow__handle{
width: 5px;
height: 5px;
border-radius: 100px;
}
/* make edges a bit thicker */
.react-flow__edge > path {
stroke: gray;
stroke-width: 3px;
}
Last but not least, we can create our reactflow like this:
reactflow(
nodes = nodes,
edges = edges,
mini_map(
pannable = TRUE,
zoomable = TRUE,
nodeColor = JS("
function nodeColor(node) {
switch (node.data.status) {
case 'uptodate':
return 'mediumseagreen';
default:
return 'lightcoral';
}
}")
),
controls(),
background(),
width = "100%",
allow_edge_connection = FALSE,
use_dagre = TRUE,
dagre_direction = "LR",
dagre_config = list(nodeWidth = 200, nodeHeight = 50)
)