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)