purrr :: reduce / reduce2或映射mutate_at()? - 应用于各列的功能

问题描述 投票:1回答:5

我有一个我想要应用于各自列的函数映射。 有什么东西喜欢映射的mutate_at

my_map <- 
  data_frame(col = names(iris)[-5],
             calc = rep(c("floor", "ceiling"), 2))
my_map 
# A tibble: 4 x 2
col          calc   
<chr>        <chr>  
Sepal.Length floor  
Sepal.Width  ceiling
Petal.Length floor  
Petal.Width  ceiling

尝试失败:

tbl_df(iris) %>% mutate_at(vars(col_calcs$col), funs_(col_calcs$calc))

Sepal.Length Sepal.Width Petal.Length Petal.Width Species Sepal.Length_floor Sepal.Width_floor Petal.Length_floor Petal.Width_floor Sepal.Length_ceiling
      <dbl>       <dbl>        <dbl>       <dbl> <fct>                <dbl>             <dbl>              <dbl>             <dbl>                <dbl>
       5.1         3.5          1.4         0.2 setosa                   5                 3                  1                 0                    6
       4.9         3            1.4         0.2 setosa                   4                 3                  1                 0                    5

期望的输出:

Sepal.Length Sepal.Width Petal.Length Petal.Width Species
        <dbl>       <dbl>        <dbl>       <dbl> <fct>  
         5.0         4.0          1.0         1.0 setosa 
         4.0         3.0          1.0         1.0 setosa

最后,my_map$calc可能具有可能应用的未知功能。 例)有人可以将最后一个“楼层”改为“圆形”。

r dplyr reduce purrr mutate
5个回答
6
投票

我不认为用dplyr::mutate_*函数可以直接做到这一点;一个解决方法是逐个使用reduce(或reduce2)函数和mutate列以及相应的转换函数:

library(tidyverse)

reduce2(.x = my_map$col, 
        .y = my_map$calc, 
        .f = function(df, col, f) mutate_at(df, vars(col), f), 
        .init = iris) %>% head(2)

#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1            5           4            1           1  setosa
# 2            4           3            1           1  setosa

1
投票

这是一种使用map2替换每列的方法。

library(tidyverse)

iris2 <- iris

iris2[, -5] <- map2(my_map$calc, my_map$col, function(x, y){
  x2 <- eval(parse(text = x))
  y2 <- iris2[[y]]
  result <- x2(y2)
  return(result)
})

head(iris2)
#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1            5           4            1           1  setosa
# 2            4           3            1           1  setosa
# 3            4           4            1           1  setosa
# 4            4           4            1           1  setosa
# 5            5           4            1           1  setosa
# 6            5           4            1           1  setosa

1
投票

我们可以从my_map开始:

library(tidyverse)
map2(my_map$col,my_map$calc,~transmute_at(iris,.x,.y)) %>%
  bind_cols(iris[!names(iris) %in% my_map$col]) %>% # or less general: iris[-5]  
  head

#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1            5           4            1           1  setosa
# 2            4           3            1           1  setosa
# 3            4           4            1           1  setosa
# 4            4           4            1           1  setosa
# 5            5           4            1           1  setosa
# 6            5           4            1           1  setosa

0
投票

如果我们假设您想要使用floor函数的所有变量包含相同的字符,即Length,并且您想要使用ceiling函数的所有变量包含相同的字符,即Width,那么我们可以应用以下代码:

library(tidyverse)
iris %>% 
  mutate_at(vars(ends_with("Length")), funs(floor)) %>% 
  mutate_at(vars(ends_with("Width")), funs(ceiling))

#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1            5           4            1           1  setosa
# 2            4           3            1           1  setosa

0
投票

虽然冗长,但我发现以下内容非常易读并且是一个简单的map实现:

iris2 <- iris %>% 
    mutate(id = 1:n()) %>%
    gather(key = col, value, my_map$col ) %>%
    full_join(my_map, by = "col") %>%
    mutate(value = invoke_map(.f = calc, .x = value)) %>%
    unnest() %>%
    select(-calc) %>
    spread(col, value) %>%
    select(-id)

head(iris2)
#    Species Petal.Length Petal.Width Sepal.Length Sepal.Width
# 1  setosa            1           1            5           4
# 2  setosa            1           1            4           3
# 3  setosa            1           1            4           4
# 4  setosa            1           1            4           4
# 5  setosa            1           1            5           4
© www.soinside.com 2019 - 2024. All rights reserved.