forked from ropensci/drake
-
Notifications
You must be signed in to change notification settings - Fork 0
DSL Proposal
Jasper edited this page Sep 7, 2018
·
4 revisions
plan <- plan_df(
# This is all basic stuff that doesn't require
# any expansion magic
small = simulate(48),
large = simulate(64),
# This is similar to rules
# in drake::evaluate_plan(rules = ), but these
# are actually *targets*! `reg` and `summary_fun`
# depend only on non-target things, while
# `dataset` references other targets.
dataset = list(small, large),
reg = list(reg1, reg2),
summary_fun = list(coefficients, residuals)
) %>%
expand_targets(
dataset,
reg,
summary_fun
) %>%
# At this point, dataset, `reg` and `summary_fun`
# all refer to a *particular item* from the
# expanded list()
add_to_plan(
analysis = reg(dataset),
summaries = summary_fun(dataset, analysis)
) %>%
collapse_targets(reg) %>%
# At this point, all targets downstream from `reg`
# (in this case `reg`, `analysis`, and `summaries`)
# are collapsed into a list() containing both permutations
# of `reg`.
add_to_plan(
# Maybe like this? Depending on what you want to do.
winner = summaries == max(unlist(summaries))
# `winner` is a logical vector indicating which reg had the
# highest associated summary value.
)
readd(winner)
# Error: must specify on which expanded dimensions -- `dataset`, `summary_fun`
# -- or use all_dim = T
# Pass in a string / symbol of the expanded target, or a literal object? IDK
readd(winner, dataset = "small", summary_fun = coefficients)###########
## SETUP ##
###########
library(tidyverse)
library(rlang)
# This might not be a good data structure? IDK
plan_df <- function(...) {
enexprs(...) %>%
# Wrap each item up as a list
map(list) %>%
as_tibble()
}
add_to_plan <- function(plan, ...) {
bind_cols(plan, plan_df(...))
}
expand_targets <- function(plan, ...) {
plan %>%
# Add expansions onto the queue in the "expand" attribute
set_attrs(expand = c(plan %@% "expand", enexprs(...)))
}
# Eval list turns expr(list(a, b)) into list(expr(a), expr(b))
# This is just to prototype expansion functionality. Drake would have to
# be smarter about when/how to expand stuff.
eval_list <- function(call) {
switch_type(call,
symbol = ,
logical = ,
integer = ,
double = ,
complex = ,
string = ,
character = call,
"NULL" = NULL,
language = if (call_name(call) %in% c("list", "c")) {
args <- call_args(call) %>% map(eval_list)
invoke(call_fn(call), .args = args)
} else {
call
},
list = map(call, eval_list),
abort(glue("Unknown input type: ", class(call)))
)
}
collect_plan <- function(plan) {
if (!length(plan %@% "expand"))
return(plan)
var <- plan %@% "expand" %>% first()
plan %>%
# Unnest the first var on the queue
unnest(!!var := eval_list(!!var), .drop = F) %>%
# Drop it off the queue
set_attrs(expand = plan %@% "expand" %>% tail(-1)) %>%
# Recurse in case there are any more expansions to do
collect_plan()
}
#################
## APPLICATION ##
#################
basic_plan <- plan_df(
# This is all basic stuff that doesn't require
# any expansion magic
small = simulate(48),
large = simulate(64),
# This is similar to rules
# in drake::evaluate_plan(rules = ), but these
# are actually *targets*! `reg` and `summary_fun`
# depend only on non-target things, while
# `dataset` references other targets.
dataset = list(small, large),
reg = list(reg1, reg2),
summary_fun = list(coefficients, residuals)
)
collect_plan(basic_plan) %>% View()
#> Classes 'tbl_df', 'tbl' and 'data.frame': 1 obs. of 5 variables:
#> $ small :List of 1
#> ..$ : language simulate(48)
#> $ large :List of 1
#> ..$ : language simulate(64)
#> $ dataset :List of 1
#> ..$ : language list(small, large)
#> $ reg :List of 1
#> ..$ : language list(reg1, reg2)
#> $ summary_fun:List of 1
#> ..$ : language list(coefficients, residuals)
expanded_plan <- basic_plan %>%
expand_targets(
dataset,
reg,
summary_fun
) %>%
# At this point, dataset, `reg` and `summary_fun`
# all refer to a *particular item* from the
# expanded list()
add_to_plan(
analysis = reg(dataset),
summaries = summary_fun(dataset, analysis)
)
collect_plan(expanded_plan) %>% View()
#> Classes 'tbl_df', 'tbl' and 'data.frame': 8 obs. of 7 variables:
#> $ small :List of 8
#> ..$ : language simulate(48)
#> ..$ : language simulate(48)
#> ..$ : language simulate(48)
#> ..$ : language simulate(48)
#> ..$ : language simulate(48)
#> ..$ : language simulate(48)
#> ..$ : language simulate(48)
#> ..$ : language simulate(48)
#> $ large :List of 8
#> ..$ : language simulate(64)
#> ..$ : language simulate(64)
#> ..$ : language simulate(64)
#> ..$ : language simulate(64)
#> ..$ : language simulate(64)
#> ..$ : language simulate(64)
#> ..$ : language simulate(64)
#> ..$ : language simulate(64)
#> $ analysis :List of 8
#> ..$ : language reg(dataset)
#> ..$ : language reg(dataset)
#> ..$ : language reg(dataset)
#> ..$ : language reg(dataset)
#> ..$ : language reg(dataset)
#> ..$ : language reg(dataset)
#> ..$ : language reg(dataset)
#> ..$ : language reg(dataset)
#> $ summaries :List of 8
#> ..$ : language summary_fun(dataset, analysis)
#> ..$ : language summary_fun(dataset, analysis)
#> ..$ : language summary_fun(dataset, analysis)
#> ..$ : language summary_fun(dataset, analysis)
#> ..$ : language summary_fun(dataset, analysis)
#> ..$ : language summary_fun(dataset, analysis)
#> ..$ : language summary_fun(dataset, analysis)
#> ..$ : language summary_fun(dataset, analysis)
#> $ dataset :List of 8
#> ..$ : symbol small
#> ..$ : symbol small
#> ..$ : symbol small
#> ..$ : symbol small
#> ..$ : symbol large
#> ..$ : symbol large
#> ..$ : symbol large
#> ..$ : symbol large
#> $ reg :List of 8
#> ..$ : symbol reg1
#> ..$ : symbol reg1
#> ..$ : symbol reg2
#> ..$ : symbol reg2
#> ..$ : symbol reg1
#> ..$ : symbol reg1
#> ..$ : symbol reg2
#> ..$ : symbol reg2
#> $ summary_fun:List of 8
#> ..$ : symbol coefficients
#> ..$ : symbol residuals
#> ..$ : symbol coefficients
#> ..$ : symbol residuals
#> ..$ : symbol coefficients
#> ..$ : symbol residuals
#> ..$ : symbol coefficients
#> ..$ : symbol residuals
#> - attr(*, "expand")= Named list()
# IDK how to prototype this yet
collapsed_plan <- expanded_plan %>%
collapse_targets(reg) %>%
# At this point, all targets downstream from `reg`
# (in this case `reg`, `analysis`, and `summaries`)
# are collapsed into a list() containing both permutations
# of `reg`.
add_to_plan(
# Maybe like this? Depending on what you want to do.
winner = summaries == max(unlist(summaries))
# `winner` is a logical vector indicating which reg had the
# highest associated summary value.
)