-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathtar_map.R
176 lines (170 loc) · 6.09 KB
/
tar_map.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#' @title Static branching.
#' @export
#' @family branching
#' @description Define multiple new targets based on existing target objects.
#' @details `tar_map()` creates collections of new
#' targets by iterating over a list of arguments
#' and substituting symbols into commands and pattern statements.
#' @section Target objects:
#' Most `tarchetypes` functions are target factories,
#' which means they return target objects
#' or lists of target objects.
#' Target objects represent skippable steps of the analysis pipeline
#' as described at <https://books.ropensci.org/targets/>.
#' Please read the walkthrough at
#' <https://books.ropensci.org/targets/walkthrough.html>
#' to understand the role of target objects in analysis pipelines.
#'
#' For developers,
#' <https://wlandau.github.io/targetopia/contributing.html#target-factories>
#' explains target factories (functions like this one which generate targets)
#' and the design specification at
#' <https://books.ropensci.org/targets-design/>
#' details the structure and composition of target objects.
#' @return A list of new target objects. If `unlist` is `FALSE`,
#' the list is nested and sub-lists are named and grouped by the original
#' input targets. If `unlist = TRUE`, the return value is a flat list of
#' targets named by the new target names.
#' See the "Target objects" section for background.
#' @param values Named list or data frame with values to iterate over.
#' The names are the names of symbols in the commands and pattern
#' statements, and the elements are values that get substituted
#' in place of those symbols. Elements of the `values` list
#' should be small objects that can easily deparse to names,
#' such as characters, integers, and symbols.
#' For more complicated elements of `values`, such as
#' lists with multiple numeric vectors,
#' `tar_map()` attempts to parse the elements into expressions,
#' but this process is not perfect, and the default
#' target names come out garbled.
#' To create a list of symbols as a column of `values`,
#' use `rlang::syms()`.
#' @param ... One or more target objects or list of target objects.
#' Lists can be arbitrarily nested, as in `list()`.
#' @param names Subset of `names(values)`
#' used to generate the suffixes in the names of the new targets.
#' You can supply symbols, a character vector,
#' or tidyselect helpers like [starts_with()].
#' @param unlist Logical, whether to flatten the returned list of targets.
#' If `unlist = FALSE`, the list is nested and sub-lists
#' are named and grouped by the original input targets.
#' If `unlist = TRUE`, the return value is a flat list of targets
#' named by the new target names.
#' @examples
#' if (identical(Sys.getenv("TAR_LONG_EXAMPLES"), "true")) {
#' targets::tar_dir({ # tar_dir() runs code from a temporary directory.
#' targets::tar_script({
#' list(
#' tarchetypes::tar_map(
#' list(a = c(12, 34), b = c(45, 78)),
#' targets::tar_target(x, a + b),
#' targets::tar_target(y, x + a, pattern = map(x))
#' )
#' )
#' })
#' targets::tar_manifest()
#' })
#' }
tar_map <- function(
values,
...,
names = tidyselect::everything(),
unlist = FALSE
) {
targets <- unlist(list(...), recursive = TRUE) %|||% list()
targets::tar_assert_target_list(targets)
assert_values_list(values)
names_quosure <- rlang::enquo(names)
names <- eval_tidyselect(names_quosure, base::names(values))
values <- tibble::as_tibble(values)
values <- tar_map_process_values(values)
values <- tar_map_extend_values(targets, values, names)
out <- lapply(targets, tar_map_target, values = values)
flat <- unlist(out, recursive = TRUE)
if_any(
unlist,
set_names(flat, map_chr(flat, ~.x$settings$name)),
set_names(out, map_chr(targets, ~.x$settings$name))
)
}
tar_map_process_values <- function(values) {
for (name in names(values)) {
values[[name]] <- map(
values[[name]],
~parse(text = targets::tar_deparse_safe(.x))[[1]]
)
}
values
}
tar_map_extend_values <- function(targets, values, names) {
suffix <- tar_map_produce_suffix(values, names)
for (target in targets) {
name <- target$settings$name
targets::tar_assert_not_in(
name,
names(values),
paste("target", name, "cannot be in names(values).")
)
values[[name]] <- as_symbols(make.names(paste(name, suffix, sep = "_")))
}
values
}
tar_map_produce_suffix <- function(values, names) {
data <- values[names] %||% tar_map_default_suffixes(values)
data <- map(data, ~as.character(unlist(.x)))
out <- apply(as.data.frame(data), 1, paste, collapse = "_")
out <- gsub("'", "", out)
out <- gsub("\"", "", out)
make.unique(out, sep = "_")
}
tar_map_default_suffixes <- function(values) {
id <- apply(
X = values,
MARGIN = 1,
FUN = digest::digest,
algo = "xxhash32"
)
list(id = id)
}
tar_map_target <- function(target, values) {
lapply(
transpose(values),
tar_map_iter,
target = target,
command = target$command$expr,
pattern = target$settings$pattern
)
}
tar_map_iter <- function(values, target, command, pattern) {
settings <- target$settings
name <- as.character(values[[settings$name]])
command <- substitute_expr(command, values)
pattern <- substitute_expr(pattern, values) %||% NULL
targets::tar_target_raw(
name = name,
command = command,
pattern = pattern,
packages = target$command$packages,
library = target$command$library,
format = settings$format,
repository = settings$repository,
iteration = settings$iteration,
error = settings$error,
memory = settings$memory,
garbage_collection = settings$garbage_collection,
deployment = settings$deployment,
priority = settings$priority,
resources = settings$resources,
storage = settings$storage,
retrieval = settings$retrieval,
cue = targets::tar_cue(
mode = target$cue$mode,
command = target$cue$command,
depend = target$cue$depend,
format = target$cue$format,
repository = target$cue$repository,
iteration = target$cue$iteration,
file = target$cue$file
)
)
}