10.3 map系列函数

10.3.1 用法

  • map()返回列表(list)
  • map_lgl(),map_int(),map_dbl(),map_chr()返回指定类型的原子向量,即逻辑型,整数型,实数型,字符型等。
  • map_dfr()map_dfc()返回通过指定的行连接或列连接创建的数据框(data.frame)
map(.x, .f, ...)

map_lgl(.x, .f, ...)

map_chr(.x, .f, ...)

map_int(.x, .f, ...)

map_dbl(.x, .f, ...)

map_raw(.x, .f, ...)

map_dfr(.x, .f, ..., .id = NULL)

map_dfc(.x, .f, ...)

在 purrr 中,参数.f 可以是一个 function,formula or vector,像 ~ .x + 2 这种形式的公式会被转换成 function 使用,关于参数的引用有如下三种方式:

  1. 对一个参数,使用 . 即可
  2. 两个参数,使用 .x 和 .y
  3. 对于更多参数,使用..1,..2,..3等等

对于像 ~ .x + 2 会转换成:

function(x){
  x + 2
}
#> function(x){
#>   x + 2
#> }

# ~ .x + . y

function(x,y){
  x + y
}
#> function(x,y){
#>   x + y
#> }
x <- list(1, 1, 1)
y <- list(10, 20, 30)
z <- list(100, 200, 300)

pmap(list(x,y,z),~ (..1 + ..2 ) * ..3)
#> [[1]]
#> [1] 1100
#> 
#> [[2]]
#> [1] 4200
#> 
#> [[3]]
#> [1] 9300

~ (..1 + ..2 ) * ..3转换成:

function(x,y,z){
  (x + y) * z
}
#> function(x,y,z){
#>   (x + y) * z
#> }

# pmap 实现
pmap(list(x,y,z),function(x,y,z) ( x + y ) *z)
#> [[1]]
#> [1] 1100
#> 
#> [[2]]
#> [1] 4200
#> 
#> [[3]]
#> [1] 9300

对于 purrr 风格的公式,我们可以看自己心情,可以学习这种方式也可以不学,因为用 R 里面的 function 也可以完全满足需求

  • map_chr

map_chr(.x, .f) ,map_chr 返回对象为字符串

map_chr(letters,paste,'yufei-world',sep='-')
#>  [1] "a-yufei-world" "b-yufei-world" "c-yufei-world" "d-yufei-world"
#>  [5] "e-yufei-world" "f-yufei-world" "g-yufei-world" "h-yufei-world"
#>  [9] "i-yufei-world" "j-yufei-world" "k-yufei-world" "l-yufei-world"
#> [13] "m-yufei-world" "n-yufei-world" "o-yufei-world" "p-yufei-world"
#> [17] "q-yufei-world" "r-yufei-world" "s-yufei-world" "t-yufei-world"
#> [21] "u-yufei-world" "v-yufei-world" "w-yufei-world" "x-yufei-world"
#> [25] "y-yufei-world" "z-yufei-world"

10.3.2 案例

  • map

map()函数返回列表,如下:

1:10 %>%
  map(rnorm, n = 10)

# 用匿名函数
1:10 %>%
  map(function(x) rnorm(10, x))

# purrr 风格公式
1:10 %>%
  map(~ rnorm(10, .x))

使用map()提取列表对象中的列表元素:

l2 <- list(
  list(num = 1:3,     letters[1:3]),
  list(num = 101:103, letters[4:6]),
  list()
)
l2 %>% map(c(2, 2))
#> [[1]]
#> [1] "b"
#> 
#> [[2]]
#> [1] "e"
#> 
#> [[3]]
#> NULL

提取每个列表中的元素,上述代码表示提取列表中第二个对象的第二位置的元素:

list(num = 1:3,letters[1:3])[[2]][2]
#> [1] "b"
list(num = 101:103, letters[4:6])[[2]][2]
#> [1] "e"

通过list构建由“名称”和“数字”构建的提取器提取元素:

l2 %>% map(list("num", 3))
#> [[1]]
#> [1] 3
#> 
#> [[2]]
#> [1] 103
#> 
#> [[3]]
#> NULL
l2 %>% map_int(list("num", 3), .default = NA)
#> [1]   3 103  NA
  • map_dbl

map_dbl()返回数字向量(双精度)。

1:10 %>%
  map(rnorm, n = 10) %>%  #输出列表
  map_dbl(mean)
#>  [1]  0.559  1.821  2.876  4.152  5.116  6.127  6.911  8.281  9.237 10.627

在数据框上应用,由于数据框(data.frame)可以看成是特殊的列表,df[[1]],df[[2]],df[[3]]等。

mtcars %>% map_dbl(sum)
#>  mpg  cyl disp   hp drat   wt qsec   vs   am gear carb 
#>  643  198 7383 4694  115  103  571   14   13  118   90

上述计算可以理解为计算:sum(mtcars[[1]]),sum(mtcars[[2]]),sum(mtcars[[3]])等。

  • map_chr

map_chr()返回字符向量。

c("foo", "bar") %>% map_chr(paste0, ":suffix")
#> [1] "foo:suffix" "bar:suffix"

favorite_desserts <- list(Sophia = "banana bread", Eliott = "pancakes", Karina = "chocolate cake")
favorite_desserts %>% map_chr(~ paste(.x, "rocks!"))
#>                  Sophia                  Eliott                  Karina 
#>   "banana bread rocks!"       "pancakes rocks!" "chocolate cake rocks!"
  • map_int

通过名字或者是数字位置提取内容。

l1 <- list(list(a = 1L), list(a = NULL, b = 2L), list(b = 3L))
l1 %>% map("a", .default = "???")
#> [[1]]
#> [1] 1
#> 
#> [[2]]
#> [1] "???"
#> 
#> [[3]]
#> [1] "???"
l1 %>% map_int("b", .default = NA)
#> [1] NA  2  3
l1 %>% map_int(2, .default = NA)
#> [1] NA  2 NA
  • map_df

map_df(.x, .f),map_df 返回对象为数据框,类似函数 map_dfr(.x,.f),map_dfc(.x,.f)

map_df()函数示例

map_df(c(1, 4, 7), function(.x) {
  return(data.frame(old_number = .x, 
                    new_number = addTen(.x)))
})
#>   old_number new_number
#> 1          1         11
#> 2          4         14
#> 3          7         17


make_dataframe <- function(x){
  data.frame(old_number = x,new_number = addTen(x))
}

map_dfr(c(1,4,7),make_dataframe)
#>   old_number new_number
#> 1          1         11
#> 2          4         14
#> 3          7         17

数据导入导出章节中提到的批量读取,通过指定全部要读取的文件列表,使用map_dfr()函数读取全部文件并合并数据源。

allfiles <- list.files(path = './data/read-write/',pattern = '^[a-z].xlsx$',full.names = T)
purrr::map_dfr(allfiles,read_excel)

在我实际工作中,实现传统零售行业门店之间商品调拨时就可使用该函数,更多详细信息可以查看我的语雀笔记。