Skip to contents

Three example targets pipelines, each written with targets::tar_script() and plotted in color (the default coloring, or by type for the branching one) and by build status, plus a few random graphs at different sizes. The last section collapses two of them: the analysis pipeline into its stages, and a random graph into blocks of nodes.

basic: about 6 targets, a straight chain

basic_dir <- tempfile("basic")
dir.create(basic_dir)
withr::with_dir(
  basic_dir,
  targets::tar_script({
    tar_option_set(packages = character())

    read_data <- function(n) {
      data.frame(x = seq_len(n), y = seq_len(n) * 2 + stats::rnorm(n))
    }

    clean_data <- function(data) {
      data[stats::complete.cases(data), ]
    }

    fit_model <- function(data) {
      stats::lm(y ~ x, data = data)
    }

    summarize_model <- function(model) {
      data.frame(
        term = names(stats::coef(model)),
        estimate = unname(stats::coef(model))
      )
    }

    write_report <- function(summary) {
      paste(utils::capture.output(print(summary)), collapse = "\n")
    }

    list(
      tar_target(n, 20),
      tar_target(raw_data, read_data(n)),
      tar_target(clean, clean_data(raw_data)),
      tar_target(model, fit_model(clean)),
      tar_target(summary, summarize_model(model)),
      tar_target(report, write_report(summary))
    )
  })
)
withr::with_dir(basic_dir, tar_ggplot())

The basic example pipeline: a short chain of six targets.

withr::with_dir(basic_dir, {
  targets::tar_make(reporter = "silent")
  tar_ggplot(node_color = "status", palette = targets_status_colors())
})

The basic pipeline after building it, colored by status (all uptodate).

branching: dynamic branching over groups

tar_network() shows a dynamic branching target’s individual branches as one node (with a branches count), so the plot has the same size however many branches each target has at runtime. The branched targets have type = "pattern", and are drawn as squares, where the other targets are circles.

branching_dir <- tempfile("branching")
dir.create(branching_dir)
withr::with_dir(
  branching_dir,
  targets::tar_script({
    tar_option_set(packages = character())

    make_group_data <- function(group, seed) {
      set.seed(seed)
      data.frame(group = group, value = stats::runif(5))
    }

    clean_group <- function(data, threshold) {
      data[data$value > threshold, ]
    }

    summarize_group <- function(data) {
      data.frame(group = data$group[1], mean = mean(data$value))
    }

    compute_stats <- function(data) {
      data.frame(group = data$group[1], sd = stats::sd(data$value))
    }

    merge_all <- function(summary, stats) {
      merge(summary, stats, by = "group")
    }

    overall_summary <- function(merged) {
      data.frame(
        mean_of_means = mean(merged$mean),
        mean_of_sds = mean(merged$sd)
      )
    }

    write_report <- function(merged, overall) {
      paste(
        utils::capture.output(print(merged)),
        utils::capture.output(print(overall)),
        collapse = "\n"
      )
    }

    list(
      tar_target(seed, 1),
      tar_target(threshold, 0.05),
      tar_target(groups, letters[1:6]),
      tar_target(
        group_data,
        make_group_data(groups, seed),
        pattern = map(groups)
      ),
      tar_target(
        cleaned,
        clean_group(group_data, threshold),
        pattern = map(group_data)
      ),
      tar_target(
        group_summary,
        summarize_group(cleaned),
        pattern = map(cleaned)
      ),
      tar_target(group_stats, compute_stats(cleaned), pattern = map(cleaned)),
      tar_target(merged, merge_all(group_summary, group_stats)),
      tar_target(overall, overall_summary(merged)),
      tar_target(report, write_report(merged, overall))
    )
  })
)
withr::with_dir(branching_dir, tar_ggplot(node_color = "type"))

The branching example pipeline, targets colored by whether they are a plain target (stem, a circle) or a dynamically branched one (pattern, a square), with one legend for color and shape.

withr::with_dir(branching_dir, {
  targets::tar_make(reporter = "silent")
  tar_ggplot(node_color = "status", palette = targets_status_colors())
})
#> + group_data declared [6 branches]
#> + cleaned declared [6 branches]
#> + group_stats declared [6 branches]
#> + group_summary declared [6 branches]

The branching pipeline after building it, colored by status.

analysis: about 20 targets, several parallel branches that merge

analysis_dir <- tempfile("analysis")
dir.create(analysis_dir)
withr::with_dir(
  analysis_dir,
  targets::tar_script({
    tar_option_set(packages = character())

    make_config <- function() list(scale = 2, offset = 1)

    load_raw <- function(seed) {
      set.seed(seed)
      stats::rnorm(50)
    }

    clean <- function(x) {
      x[abs(x) < 3]
    }

    feature <- function(x, fn) {
      fn(x)
    }

    combine2 <- function(a, b) {
      c(a, b)
    }

    model_fit <- function(x, config) {
      mean(x) * config$scale + config$offset
    }

    evaluate <- function(model, other = NULL) {
      if (is.null(other)) model else mean(c(model, other))
    }

    report_all <- function(config, ...) {
      vals <- c(...)
      paste(
        "config:",
        config$scale,
        config$offset,
        "| results:",
        paste(round(vals, 3), collapse = ", ")
      )
    }

    # `config` comes first, before any description is set, so it has none
    config_targets <- list(tar_target(config, make_config()))

    tar_option_set(description = "load")
    load_targets <- list(
      tar_target(raw_a, load_raw(1)),
      tar_target(raw_b, load_raw(2)),
      tar_target(raw_c, load_raw(3))
    )

    tar_option_set(description = "clean")
    clean_targets <- list(
      tar_target(clean_a, clean(raw_a)),
      tar_target(clean_b, clean(raw_b)),
      tar_target(clean_c, clean(raw_c))
    )

    tar_option_set(description = "features")
    feature_targets <- list(
      tar_target(feature_a1, feature(clean_a, mean)),
      tar_target(feature_a2, feature(clean_a, stats::median)),
      tar_target(feature_b1, feature(clean_b, mean)),
      tar_target(feature_ab, combine2(feature_a1, feature_b1)),
      tar_target(feature_bc, combine2(clean_b, clean_c))
    )

    tar_option_set(description = "model")
    model_targets <- list(
      tar_target(model_1, model_fit(combine2(feature_a2, feature_ab), config)),
      tar_target(model_2, model_fit(combine2(feature_ab, feature_bc), config)),
      tar_target(model_3, model_fit(combine2(feature_bc, clean_c), config))
    )

    tar_option_set(description = "evaluate")
    eval_targets <- list(
      tar_target(eval_1, evaluate(model_1)),
      tar_target(eval_2, evaluate(model_2, model_1)),
      tar_target(eval_3, evaluate(model_3))
    )

    tar_option_set(description = "report")
    report_targets <- list(
      tar_target(summary_all, combine2(combine2(eval_1, eval_2), eval_3)),
      tar_target(report, report_all(config, summary_all))
    )

    tar_option_set(description = character(0))
    c(
      config_targets,
      load_targets,
      clean_targets,
      feature_targets,
      model_targets,
      eval_targets,
      report_targets
    )
  })
)

The targets have a stage in their description, set a block at a time with tar_option_set(). It doesn’t change the plots below, but the collapsed graphs at the end of this page group the targets by it.

withr::with_dir(analysis_dir, tar_ggplot())

The analysis example pipeline: about 20 targets with several parallel chains that cross and merge, showing crossings and bridges.

withr::with_dir(analysis_dir, {
  targets::tar_make(reporter = "silent")
  tar_ggplot(node_color = "status", palette = targets_status_colors())
})

The analysis pipeline after building it, colored by status.

Random graphs at different sizes

random_dag <- function(seed, n, p) {
  set.seed(seed)
  names <- sample(sprintf("n%02d", seq_len(n)))
  from <- to <- character(0)
  for (i in seq_len(n)) {
    for (j in seq_len(n)) {
      if (i < j && runif(1) < p) {
        from <- c(from, sprintf("n%02d", i))
        to <- c(to, sprintf("n%02d", j))
      }
    }
  }
  as_dag(data.frame(from = from, to = to), nodes = data.frame(name = names))
}
ggplot_dag(random_dag(1, 6, 0.3))

A small random graph with 6 nodes.

ggplot_dag(random_dag(2, 15, 0.15))

A medium random graph with 15 nodes and moderate edge density.

ggplot_dag(random_dag(3, 30, 0.08))

A larger random graph with 30 nodes, showing several crossings and bridges.

ggplot_dag(random_dag(4, 20, 0.3))

A densely connected random graph with 20 nodes and high edge probability, showing many crossings.

ggplot_dag(random_dag(5, 25, 0.04))

A sparse random graph with 25 nodes and low edge probability, mostly independent chains.

ggplot_dag(
  random_dag(6, 10, 0.2),
  style = style_metro(node_radius = 8, arc_radius = 18)
)

A random graph in a custom style: larger nodes, wider arcs.

Collapsed graphs

tar_ggplot(collapse_by = ) and dag_collapse() merge the nodes that share a value of a column into one node, labeled with how many it stands for, and draw one edge for each pair of groups. See the Collapsing a pipeline article for how stages are set and what can go wrong.

analysis by stage

Collapsing the analysis pipeline above by its description gives one node per stage, with the number of targets in the label. config, which has no stage, stays as it is:

withr::with_dir(analysis_dir, tar_ggplot(collapse_by = "description"))

The analysis pipeline collapsed to seven nodes, one per row: config, load (3), clean (3), features (5), model (3), evaluate (3) and report (2), joined by lines that bend and cross.

The size of a node and the width of an edge can follow n, the number of targets in a node and of dependencies in an edge, and node_color = "description" colors the nodes by stage:

withr::with_dir(
  analysis_dir,
  tar_ggplot(
    collapse_by = "description",
    node_color = "description",
    node_size = "n",
    edge_width = "n"
  )
)

The same seven nodes, now of different sizes, the largest for features and the smallest for config, joined by lines of different widths. Each stage has a color, named in a legend, and config, which has no stage, is gray.

A random graph by block

Any DAG can be collapsed by a column of its nodes. Here the 30-node graph from above is grouped in blocks of five consecutive numbers (n01 to n05 are block A, and so on). The edges of a random graph run from lower to higher numbers, so the blocks can’t make a cycle. edge_width = "n" shows how many edges each line stands for:

d <- random_dag(3, 30, 0.08)
d$nodes$block <- LETTERS[(as.integer(substring(d$nodes$name, 2)) - 1) %/% 5 + 1]
ggplot_dag(dag_collapse(d, "block"), edge_width = "n")

The 30-node random graph collapsed to six nodes, A (5), B (5), C (5), D (5), F (5) and E (5), one per row, joined by lines of different widths.