Skip to content
Jasper edited this page Sep 7, 2018 · 4 revisions

Example Usage

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)

Semi-functional prototype

###########
## 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()

image

#> 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()

image

#> 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.
  )

Clone this wiki locally