我正在尝试加强%>%
管道工作流程,在该工作流程中,我需要将相同的功能应用于多个列,但每次都要更改一个参数。我觉得purrr
的{{1}}或map
函数应该有帮助,但是我无法解决这个问题。
我的数据框包含预期寿命,贫困率和家庭收入中位数的列。我可以将所有这些列名称传递给invoke
中的vars
,使用mutate_at
作为应用于每个列的函数,并可选地提供一个round
参数。但是我无法找出一种方法来为与每一列关联的digits
传递不同的值(如果存在)。我希望将预期寿命四舍五入为1位数,将贫困数舍入为2位数,将收入四舍五入为整数。
我可以在每一列上调用digits
,但是鉴于我可能有更多的列都接受了相同的函数,而只是更改了一个附加参数,所以我想更简洁一些。
mutate
在我的想象中,我可以做这样的事情:
library(tidyverse)
df <- tibble::tribble(
~name, ~life_expectancy, ~poverty, ~household_income,
"New Haven", 78.0580437642378, 0.264221051111753, 42588.7592521085
)
但是得到错误
mutate_impl(.data,点)中的错误:
df %>% mutate_at(vars(life_expectancy, poverty, household_income), round, digits = c(1, 2, 0))
列的长度必须为1(行数),而不是3
使用life_expectancy
代替mutate_at
只是为了与理想情况下的语法相同:
mutate
映射数字使用的是每个列的每个df %>%
mutate_at(vars(life_expectancy), round, digits = 1) %>%
mutate_at(vars(poverty), round, digits = 2) %>%
mutate_at(vars(household_income), round, digits = 0)
#> # A tibble: 1 x 4
#> name life_expectancy poverty household_income
#> <chr> <dbl> <dbl> <dbl>
#> 1 New Haven 78.1 0.26 42589
选项,而不是按位置,而是给我3行,每行四舍五入为不同的数字。
digits
由reprex package(v0.2.1)于2018-11-13创建
答案 0 :(得分:7)
2个解决方案
mutate
和!!!
invoke
是个好主意,但由于大多数 tidyverse
函数支持!!!
运算符,因此您现在不需要它了,这是您可以做的:>
digits <- c(life_expectancy = 1, poverty = 2, household_income = 0)
df %>% mutate(!!!imap(digits, ~round(..3[[.y]], .x),.))
# # A tibble: 1 x 4
# name life_expectancy poverty household_income
# <chr> <dbl> <dbl> <dbl>
# 1 New Haven 78.1 0.26 42589
..3
是初始数据帧,它通过调用末尾的点作为第三个参数传递给函数。
写得更明确:
df %>% mutate(!!!imap(
digits,
function(digit, name, data) round(data[[name]], digit),
data = .))
如果您需要从旧界面开始(尽管我建议的界面会更灵活),请首先执行以下操作:
digits <- setNames(c(1, 2, 0), c("life_expectancy", "poverty", "household_income"))
mutate_at
和<<-
在这里,我们尽量避免使用<<-
,但是可读性很重要,而且这真的很容易阅读。
digits <- c(1, 2, 0)
i <- 0
df %>%
mutate_at(vars(life_expectancy, poverty, household_income), ~round(., digits[i<<- i+1]))
# A tibble: 1 x 4
# name life_expectancy poverty household_income
# <chr> <dbl> <dbl> <dbl>
# 1 New Haven 78.1 0.26 42589
(如果您在我的第一个解决方案中使用命名矢量,则只需df %>% mutate_at(names(digits), ~round(., digits[i<<- i+1]))
答案 1 :(得分:2)
这是一种类似于Henrik评论的map2
解决方案。然后,您可以将其包装在自定义函数中。我提供了一个粗略的第一次尝试,但是我做了最少的测试,因此如果评估很奇怪,它可能会在各种情况下中断。它也没有为.at
使用tidyselect,但是modify_at
也没有使用...
library(tidyverse)
df <- tibble::tribble(
~name, ~life_expectancy, ~poverty, ~household_income,
"New Haven", 78.0580437642378, 0.264221051111753, 42588.7592521085,
"New York", 12.349685329, 0.324067934, 32156.230974623
)
rounded <- df %>%
select(life_expectancy, poverty, household_income) %>%
map2_dfc(
.y = c(1, 2, 0),
.f = ~ round(.x, digits = .y)
)
df %>%
select(-life_expectancy, -poverty, -household_income) %>%
bind_cols(rounded)
#> # A tibble: 2 x 4
#> name life_expectancy poverty household_income
#> <chr> <dbl> <dbl> <dbl>
#> 1 New Haven 78.1 0.26 42589
#> 2 New York 12.3 0.32 32156
modify2_at <- function(.x, .y, .at, .f) {
modified <- .x[.at] %>%
map2(.y, .f)
.x[.at] <- modified
return(.x)
}
df %>%
modify2_at(
.y = c(1, 2, 0),
.at = c("life_expectancy", "poverty", "household_income"),
.f = ~ round(.x, digits = .y)
)
#> # A tibble: 2 x 4
#> name life_expectancy poverty household_income
#> <chr> <dbl> <dbl> <dbl>
#> 1 New Haven 78.1 0.26 42589
#> 2 New York 12.3 0.32 32156
由reprex package(v0.2.1)于2018-11-13创建
答案 2 :(得分:2)
与tidyeval一起玩:
prepared_pairs <-
map2(
set_names(syms(list("life_expectancy", "poverty", "household_income"))),
c(1, 2, 0),
~expr(round(!!.x, digits = !!.y))
)
mutate(df, !!! prepared_pairs)
# # A tibble: 1 x 4
# name life_expectancy poverty household_income
# <chr> <dbl> <dbl> <dbl>
# 1 New Haven 78.1 0.26 42589