purrr
是一个拓展R函数式编程能力的包。它会涉及到不少东西,在这篇文章中,我会展现在purrr
中最重要的(至少对我来讲)几个函数。编程
map
函数来摆脱循环library(purrr) numbers <- list(11, 12, 13, 14) map_dbl(numbers, sqrt)
## [1] 3.316625 3.464102 3.605551 3.741657
你可能想知道为何这可能比for循环更受欢迎?由于它更简洁,你不须要初始化任何类型的结构来保存结果。若是用google “create empty list in R”,你会发现它很广泛。然而,有了map
函数族,将不须要初始化结构。map_dbl
函数会返回一个实数原子列表(atomic list),map
函数会返回一个列表,去试一下吧。less
#创造一个辅助函数,若是为偶数则返回TRUE is_even <- function(x){ !as.logical(x %% 2) } map_if(numbers, is_even, sqrt)
## [[1]] ## [1] 11 ## ## [[2]] ## [1] 3.464102 ## ## [[3]] ## [1] 13 ## ## [[4]] ## [1] 3.741657
map_at(numbers, c(1,3), sqrt)
## [[1]] ## [1] 3.316625 ## ## [[2]] ## [1] 12 ## ## [[3]] ## [1] 3.605551 ## ## [[4]] ## [1] 14
map_if()
和 map_at()
比map
拥有更多的参数;若是是map_if()
,则是用一个判断函数(一个返回TRUE
或者FALSE
的函数),map_at
则是用一个位置向量。这样只有在知足某一条件时才会映射你的函数,这也是不少人google寻找的东西。函数式编程
numbers2 <- list(1, 2, 3, 4) map2(numbers, numbers2, `+`)
## [[1]] ## [1] 12 ## ## [[2]] ## [1] 14 ## ## [[3]] ## [1] 16 ## ## [[4]] ## [1] 18
map_2
函数能够输入2个参数来函数
你可使用map_2
函数将两个列表映射到一个函数,你甚至可使用pmap()
函数将任意数量的列表映射到任何函数。oop
possible_sqrt <- possibly(sqrt, otherwise = NA_real_) numbers_with_error <- list(1, 2, 3, "spam", 4) map(numbers_with_error, possible_sqrt)
## [[1]] ## [1] 1 ## ## [[2]] ## [1] 1.414214 ## ## [[3]] ## [1] 1.732051 ## ## [[4]] ## [1] NA ## ## [[5]] ## [1] 2
另外一个很常见的问题:即便报错,也要继续执行你的循环。在大多数状况下,循环会在错误处中止,可是你想让他继续跑下去,看看哪里出错了。去google “skip error in a loop” 你会发现有不少人也想这样作。其实只要结合map()
函数与possibly()
函数就能够了。大多数解决方案能够会说使用tryCatch
函数,但我我的感受它不太好用。google
safe_sqrt <- safely(sqrt, otherwise = NA_real_) map(numbers_with_error, safe_sqrt)
## [[1]] ## [[1]]$result ## [1] 1 ## ## [[1]]$error ## NULL ## ## ## [[2]] ## [[2]]$result ## [1] 1.414214 ## ## [[2]]$error ## NULL ## ## ## [[3]] ## [[3]]$result ## [1] 1.732051 ## ## [[3]]$error ## NULL ## ## ## [[4]] ## [[4]]$result ## [1] NA ## ## [[4]]$error ## <simpleError in sqrt(x = x): non-numeric argument to mathematical function> ## ## ## [[5]] ## [[5]]$result ## [1] 2 ## ## [[5]]$error ## NULL
safely
函数与possibly
函数很类似,可是它会在列表中返回列表。所以元素是结果和伴随错误消息的列表。若是没有错误,则返回NULL
。若是有错误,则返回错误信息。atom
safe_result_list <- map(numbers_with_error, safe_sqrt) transpose(safe_result_list)
## $result ## $result[[1]] ## [1] 1 ## ## $result[[2]] ## [1] 1.414214 ## ## $result[[3]] ## [1] 1.732051 ## ## $result[[4]] ## [1] NA ## ## $result[[5]] ## [1] 2 ## ## ## $error ## $error[[1]] ## NULL ## ## $error[[2]] ## NULL ## ## $error[[3]] ## NULL ## ## $error[[4]] ## <simpleError in sqrt(x = x): non-numeric argument to mathematical function> ## ## $error[[5]] ## NULL
这里咱们转置了一个列表。这意味着咱们仍然返回列表中的列表,可是第一个列表里面全是results
,可经过safe_result_list$result
获得;第二个列表中全是errors
,能够经过 safe_result_list$error
获得,这是颇有用的。spa
transposed_list <- transpose(safe_result_list) transposed_list %>% at_depth(2, is_null)
## Warning: at_depth() is deprecated, please use `modify_depth()` instead
## $result ## $result[[1]] ## [1] FALSE ## ## $result[[2]] ## [1] FALSE ## ## $result[[3]] ## [1] FALSE ## ## $result[[4]] ## [1] FALSE ## ## $result[[5]] ## [1] FALSE ## ## ## $error ## $error[[1]] ## [1] TRUE ## ## $error[[2]] ## [1] TRUE ## ## $error[[3]] ## [1] TRUE ## ## $error[[4]] ## [1] FALSE ## ## $error[[5]] ## [1] TRUE
有时候处理列表嵌套列表的数据会很棘手,特别是当咱们想在子列表中应用一个函数时。可是使用at_depth()
函数将会变得很简单。code
name_element <- c("sqrt()", "ok?") set_names(transposed_list, name_element)
## $`sqrt()` ## $`sqrt()`[[1]] ## [1] 1 ## ## $`sqrt()`[[2]] ## [1] 1.414214 ## ## $`sqrt()`[[3]] ## [1] 1.732051 ## ## $`sqrt()`[[4]] ## [1] NA ## ## $`sqrt()`[[5]] ## [1] 2 ## ## ## $`ok?` ## $`ok?`[[1]] ## NULL ## ## $`ok?`[[2]] ## NULL ## ## $`ok?`[[3]] ## NULL ## ## $`ok?`[[4]] ## <simpleError in sqrt(x = x): non-numeric argument to mathematical function> ## ## $`ok?`[[5]] ## NULL
reduce(numbers, `*`)
## [1] 24024
下面是 accumulate()
函数:orm
accumulate(numbers, `*`)
## [1] 11 132 1716 24024
它会保存中间结果。
若用accumulate_right()
则为 从右向左
这个函数很是经常使用,你能够reduce
任何东西:
矩阵:
mat1 <- matrix(rnorm(10), nrow = 2) mat2 <- matrix(rnorm(10), nrow = 2) mat3 <- matrix(rnorm(10), nrow = 2)
list_mat <- list(mat1, mat2, mat3) reduce(list_mat, `+`)#结果等同于mat1+mat2+mat3
## [,1] [,2] [,3] [,4] [,5] ## [1,] -2.48530177 1.0110049 0.4450388 1.280802 1.3413979 ## [2,] 0.07596679 -0.6872268 -0.6579242 1.615237 0.8231933
甚至数据框:
df1 <- as.data.frame(mat1) df2 <- as.data.frame(mat2) df3 <- as.data.frame(mat3) list_df <- list(df1, df2, df3) reduce(list_df, dplyr::full_join)
## Joining, by = c("V1", "V2", "V3", "V4", "V5") ## Joining, by = c("V1", "V2", "V3", "V4", "V5") ## V1 V2 V3 V4 V5 ## 1 -0.6264538 -0.8356286 0.32950777 0.48742905 0.5757814 ## 2 0.1836433 1.5952808 -0.82046838 0.73832471 -0.3053884 ## 3 -0.8969145 1.5878453 -0.08025176 0.70795473 1.9844739 ## 4 0.1848492 -1.1303757 0.13242028 -0.23969802 -0.1387870 ## 5 -0.9619334 0.2587882 0.19578283 0.08541773 -1.2188574 ## 6 -0.2925257 -1.1521319 0.03012394 1.11661021 1.2673687
但愿你能喜欢这些有用的列变函数。
解释下map,reduce
举例说明,好比咱们有一个函数$f(x)=x^2$,要把这个函数做用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]
上,就能够用map()
实现以下:
举例说明,好比咱们有一个函数f(x)=x^2,要把这个函数做用在一个list(1, 2, 3, 4, 5, 6, 7, 8, 9)上,就能够用map()实现以下: map(list(1:9),function(x)x^2) f(x) = x * x │ │ ┌───┬───┬───┬───┼───┬───┬───┬───┐ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ [ 1 2 3 4 5 6 7 8 9 ] │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ [ 1 4 9 16 25 36 49 64 81 ]
reduce(list(x1, x2, x3, x4),f) = f(f(f(x1, x2), x3), x4)