我有一个我想要应用于各自列的函数映射。
有什么东西喜欢映射的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
可能具有可能应用的未知功能。
例)有人可以将最后一个“楼层”改为“圆形”。
我不认为用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
这是一种使用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
我们可以从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
如果我们假设您想要使用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
虽然冗长,但我发现以下内容非常易读并且是一个简单的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