按列位置将函数参数传递给mutate_at

时间:2018-11-13 19:20:23

标签: r dplyr purrr

我正在尝试加强%>%管道工作流程,在该工作流程中,我需要将相同的功能应用于多个列,但每次都要更改一个参数。我觉得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创建

3 个答案:

答案 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