Использование сложных выражений RHS в `dplyr :: case_when ()` в контексте `dplyr :: mutate ()` - PullRequest
3 голосов
/ 07 ноября 2019

Проблема

Я пытаюсь использовать dplyr::mutate() и dplyr::case_when() для создания нового столбца данных в кадре данных, который заполняется данными, хранящимися в другом объекте («список поиска»), иосновывается на информации в столбцах в кадре данных.

Я знаю, что ответ, вероятно, связан с (им) правильным использованием квазиквотирования и NSE, но у меня возникают проблемы с экстраполяцией информации в Программирование с dplyr виньетка на мойситуация.

Я надеюсь, что публикация этого репертуара может привести меня к правильному ответу, и я думаю, что решение этой проблемы во многом поможет мне разобраться с NSE.

Пример данных

key_list <- list(
  "a" = list(
    foo = 1,
    bar = 2),
  "b" = list(
    foo = 3,
    bar = 4),
  "c" = list(
    foo = 5,
    bar = 6)
  )

x <- tibble(fruit = c("apple", "orange", "grape", "apple", "apple", "orange"),
            `Old Letter` = c("a", "a", "b", "c", "c", "c"),
            `Old Number` = c(9, 8, 7, 6, 5, 4)
            )

x

# # A tibble: 6 x 3
#   fruit  `Old Letter` `Old Number`
#   <chr>  <chr>               <dbl>
# 1 apple  a                       9
# 2 orange a                       8
# 3 grape  b                       7
# 4 apple  c                       6
# 5 apple  c                       5
# 6 orange c                       4

Цель

В частности, я хочу создать новый столбец в x (который я назову `New Number`), который заполняется на основе значений в x$fruit и x$`Old Letter`.

Вот код, который имитирует, где я зависаю в моем фактическом случае использования:

x %>% mutate(`New Number` = case_when(
  fruit == "apple" ~ pluck(key_list, `Old Letter`, "foo") * 10,
  fruit == "orange" ~ pluck(key_list, `Old Letter`, "foo") * 100,
  fruit == "grape" ~ pluck(key_list, `Old Letter`, "foo") * 1000
  ))

# Error: Index 1 must have length 1, not 6

Ожидаемый результат

В моем представлении я вижу (требуемый) порядок таких операций, например, для первой строки x:

  • fruit == "apple" равен TRUE, поэтому оцените это выражение: pluck(key_list, `Old Letter`, "foo") * 10
  • Поскольку значениев столбце `Old Letter` для этой строки "a" выражение становится pluck(key_list, "a", "foo") * 10 (которое должно работать с объектом key_list в глобальной среде)
  • Это упрощается до 2 * 10, что равно 20
  • Поместите результат вычисления этого выражения в столбец `New Number`.

Экстраполируя это на всю команду, я ожидал, что это будет выглядеть как:

# # A tibble: 6 x 4
#   fruit  `Old Letter` `Old Number` `New Number`
#   <chr>  <chr>               <dbl>        <dbl>
# 1 apple  a                       9           20
# 2 orange a                       8          200
# 3 grape  b                       7         4000
# 4 apple  c                       6           60
# 5 apple  c                       5           60
# 6 orange c                       4          600

Мой дубль:

Судя по полученному сообщению об ошибке, похоже, чтоодно значение из столбца `Old Letter` используется в качестве индекса для использования pluck(), весь столбец `Old Letter` передается как вектор. Я предполагаю, что это потому, что в соответствии с документацией для case_when():

case_when() не является функцией eid.

Я пытался отследить, как это происходит, но стек трассировки, похоже, не помог мне, и завершение всей команды в rlang::qq_show() или quo() не показало мне, как интерпретировал Rкоманда относительно NSE, потому что они оба выдавали ту же ошибку.

Я экспериментировал с комбинациями:

  • quo(),
  • enquo(),
  • !!,
  • !!enquo() (сокращено до {{}}) и
  • sym()

inприведенный выше код Reprex, а также его обертывание в функцию, но он выдает ту же ошибку:

get_num <- function(x, y) purrr::pluck(key_list, x, y)

x %>% mutate(`New Number` = case_when(
  fruit == "apple" ~ get_num(`Old Letter`, "foo") * 10,
  fruit == "orange" ~ get_num(`Old Letter`, "foo") * 100,
  fruit == "grape" ~ get_num(`Old Letter`, "foo") * 1000
  ))

# Error: Index 1 must have length 1, not 6

Этот ответ на другой вопрос по SO говорит мне, что

Полагаю, что вам не хватает в case_when() того, что аргументы вычисляются сразу, а не в строке.

Но я не уверен, если / какэто относится к моей ситуации, поэтому я в растерянности.

В любом случае, спасибо за любую помощь, которую вы, ребята, можете оказать!


sessionInfo():

R version 3.6.0 (2019-04-26)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS  10.15

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.6/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] rlang_0.4.1     readxl_1.3.1    forcats_0.4.0   stringr_1.4.0   dplyr_0.8.3     purrr_0.3.3     readr_1.3.1     tidyr_1.0.0     tibble_2.1.3   
[10] ggplot2_3.2.1   tidyverse_1.2.1

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.2       cellranger_1.1.0 pillar_1.4.2     compiler_3.6.0   base64enc_0.1-3  tools_3.6.0      digest_0.6.22    zeallot_0.1.0    evaluate_0.14   
[10] lubridate_1.7.4  jsonlite_1.6     lifecycle_0.1.0  nlme_3.1-141     gtable_0.3.0     lattice_0.20-38  pkgconfig_2.0.3  cli_1.1.0        rstudioapi_0.10 
[19] yaml_2.2.0       haven_2.1.1      xfun_0.10        withr_2.1.2      xml2_1.2.2       httr_1.4.1       knitr_1.25       generics_0.0.2   vctrs_0.2.0     
[28] hms_0.5.1        grid_3.6.0       tidyselect_0.2.5 glue_1.3.1       R6_2.4.0         fansi_0.4.0      rmarkdown_1.16   modelr_0.1.5     magrittr_1.5    
[37] htmltools_0.4.0  backports_1.1.5  scales_1.0.0     rvest_0.3.4      assertthat_0.2.1 colorspace_1.4-1 utf8_1.1.4       stringi_1.4.3    lazyeval_0.2.2  
[46] munsell_0.5.0    broom_0.5.2      crayon_1.3.4 

1 Ответ

3 голосов
/ 07 ноября 2019

Я думаю, что проблема может быть связана не столько с NSE, сколько с тем, что pluck не векторизовано - как написано в настоящее время pluck не оценивается один раз для каждой строки, а вместо этого все строки пытаются пройти через pluck сразу. Однако, как вы выяснили, pluck требуется ввод одного числа, а не вектора.

Один из способов решить эту проблему - map функция через строки, используя ваш код как небольшую лямбду-функция стиляОбратите внимание, что вам нужно использовать map_dbl для приведения числового значения, иначе map вернет список, и все взорвется: -)

x %>% 
  mutate(`New Number` = case_when(
    fruit == "apple" ~ map_dbl(`Old Letter`, ~ pluck(key_list, ., "foo")) * 10,
    fruit == "orange" ~ map_dbl(`Old Letter`, ~ pluck(key_list, ., "foo")) * 100,
    fruit == "grape" ~ map_dbl(`Old Letter`, ~ pluck(key_list, ., "foo")) * 1000
  ))

# # A tibble: 6 x 4
#   fruit  `Old Letter` `Old Number` `New Number`
#   <chr>  <chr>               <dbl>        <dbl>
# 1 apple  a                       9           10
# 2 orange a                       8          100
# 3 grape  b                       7         3000
# 4 apple  c                       6           50
# 5 apple  c                       5           50
# 6 orange c                       4          500
...