Skip to content

Instantly share code, notes, and snippets.

@stephlocke

stephlocke/MoreComplex.R

Last active Apr 26, 2017
Embed
What would you like to do?
Foray into purrr for error handling
library(purrr)
library(dplyr)
#---- SETUP -----------------------------------------
# A function that takes a reasonable amount of inputs
# and errors a bunch
doA <- function(letter, types, nonvar) {
if (letter >= "c")
stop("Error")
expand.grid(letter, types, nonvar)
}
# Some initial values
letter <- letters[1:3]
types <- LETTERS[1:3]
nonvar <- 1
#---- THE PROBLEM -----------------------------------
# With safe values, we can pass our variables
# as a list using `pmap` and then consolidate with
# `bind_rows`.
list(letter[-3], types[-3], nonvar) %>%
pmap(doA) %>%
dplyr::bind_rows()
# With erroring values, we get an error
list(letter, types, nonvar) %>%
pmap(doA) %>%
dplyr::bind_rows()
#---- MAKING IT WORK --------------------------------
# Use the `safely` to produce list outputs for the
# function. Each element will contain a result and
# an error.
s_doA <- safely(doA)
# Use our safely version - no error!
list(letter, types, nonvar) %>%
pmap(s_doA) ->
int_result1
# We need to process it though. Use `map` with a
# name to extract those bits from each element.
# As the empty results aren't interesting, use
# `discard` to drop them.
int_result1 %>%
map("result") %>%
discard( ~ is_null(.x)) %>%
dplyr::bind_rows() ->
result
# We can get the error messages where applicable
# for tracking which executions errored. We use
# `map` with a vector this time for a nested selection.
# Then we need to convert the NULL elements to something
# that will stick around in a vector. We use `map_if`
# to convert all NULLs to NAs. The `as_vector` then
# simplifies the results.
int_result1 %>%
map(c("error", "message")) %>%
map_if(is_null, function(x)
NA_character_) %>%
as_vector() ->
errors
library(purrr)
library(dplyr)
#---- SETUP -----------------------------------------
generateTypeA_<-function(otherstuff){
if(rnorm(1)>.75) stop(sample(LETTERS,1))
data_frame(type="TypeA")
}
generateTypeA<-safely(generateTypeA_)
generateTypeB_<-function(otherstuff){
if(rnorm(1)>.75) stop(sample(LETTERS,1))
data_frame(type="TypeB")
}
generateTypeB<-safely(generateTypeB_)
generateTypeC_<-function(otherstuff){
if(rnorm(1)>.75) stop(sample(LETTERS,1))
data_frame(type="TypeC")
}
generateTypeC<-safely(generateTypeC_)
generateFragment_<-function(torun, otherstuff){
torun %>%
invoke_map(otherstuff)
}
generateFragment<-safely(generateFragment_)
# Some initial values
torun <-c("generateTypeA", "generateTypeB", "generateTypeC")
otherstuff<-"blah"
list(torun, otherstuff) %>%
pmap(generateFragment) ->
int_result1
int_result1 %>%
map("result") %>%
flatten() %>%
map_df("result") ->
result
int_result1 %>%
map("error") %>%
flatten ->
overallerrors
int_result1 %>%
map("result") %>%
flatten %>%
map_chr(c("error","message"), .null=NA_character_) ->
functionerrors
@stephlocke

This comment has been minimized.

Copy link
Owner Author

@stephlocke stephlocke commented Apr 23, 2017

L48 can become simply discard(is_null) %>%

@stephlocke

This comment has been minimized.

Copy link
Owner Author

@stephlocke stephlocke commented Apr 23, 2017

Courtesy of @jennybc

  • Use the crossing function on L10 to make a tibble output - no pesky strings as factors!
  • Use pmap_df instead of map %>% bind_rows in L23
  • Use compact for more succinct NULL removal in L48
  • Use the sneaky .null paramater in map_chr to do L60-61 in one step! (implemented in purrr 0.2.1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.