web-dev-qa-db-ja.com

関数の引数を列の位置でmutate_atに渡します

同じ関数を複数の列に適用する必要があるが、毎回1つの引数を変更する%>%パイプワークフローを強化しようとしています。 purrrmap関数またはinvoke関数が役立つと思いますが、頭を抱えることはできません。

私のデータフレームには、平均余命、貧困率、平均世帯収入の列があります。これらすべての列名をmutate_atvarsに渡し、roundをそれぞれに適用する関数として使用し、オプションでdigits引数を指定できます。しかし、存在する場合、各列に関連付けられているdigitsに異なる値を渡す方法を理解できません。平均余命を1桁に、貧困を2に、所得を0に丸めてください。

各列でmutateを呼び出すことができますが、追加の引数のみを変更して同じ関数を受け取る列が増える可能性があることを考えると、もっと簡潔なものにしたいと思います。

library(tidyverse)

df <- tibble::tribble(
        ~name, ~life_expectancy,          ~poverty, ~household_income,
  "New Haven", 78.0580437642378, 0.264221051111753,  42588.7592521085
  )

私の想像では、私はこのようなことをすることができました:

df %>%
  mutate_at(vars(life_expectancy, poverty, household_income), 
            round, digits = c(1, 2, 0))

しかし、エラーが発生します

Mutate_impl(.data、dot)のエラー:列life_expectancyは3ではなく長さ1(行数)でなければなりません

mutateの代わりにmutate_atを使用すると、私の理想的なケースと同じ構文になります。

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

数字のマッピングでは、位置ではなくeach列の各digitsオプションを使用して、3つの行をそれぞれ別の値に丸めます桁数。

df %>%
  mutate_at(vars(life_expectancy, poverty, household_income), 
            function(x) map(x, round, digits = c(1, 2, 0))) %>%
  unnest()
#> # A tibble: 3 x 4
#>   name      life_expectancy poverty household_income
#>   <chr>               <dbl>   <dbl>            <dbl>
#> 1 New Haven            78.1    0.3            42589.
#> 2 New Haven            78.1    0.26           42589.
#> 3 New Haven            78      0              42589

2018-11-13に reprexパッケージ (v0.2.1)によって作成されました

13
camille

2ソリューション


mutate with _!!!_

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_は、呼び出しの最後にあるドットを介して、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]))

7

これは、ヘンリックのコメントに沿った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

2018-11-13に reprexパッケージ (v0.2.1)によって作成されました

2
Calum You

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
2
Aurèle