Skip to content

Instantly share code, notes, and snippets.

@stephlocke
Last active April 26, 2017 16:44
Show Gist options
  • Save stephlocke/dc72be42e39997bef894dfc95b67fd8a to your computer and use it in GitHub Desktop.
Save stephlocke/dc72be42e39997bef894dfc95b67fd8a to your computer and use it in GitHub Desktop.
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
Copy link
Author

L48 can become simply discard(is_null) %>%

@stephlocke
Copy link
Author

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