dplyr 0.7.5 изменение в выборе()

select() в dplyr 0.7.5 возвращает другой результат из dplyr 0.7.4 при использовании именованного вектора для указания столбцов.

library(dplyr)                               
df <- data.frame(a = 1:5, b = 6:10, c = 11:15)
print(df)                                     
#>   a  b  c
#> 1 1  6 11
#> 2 2  7 12
#> 3 3  8 13
#> 4 4  9 14
#> 5 5 10 15

# a named vector
cols <- c(x = 'a', y = 'b', z = 'c')          
print(cols)                                   
#>  x   y   z 
#> "a" "b" "c"

# with dplyr 0.7.4
# returns column names with vector values
select(df, cols)                              
#>   a  b  c
#> 1 1  6 11
#> 2 2  7 12
#> 3 3  8 13
#> 4 4  9 14
#> 5 5 10 15

# with dplyr 0.7.5
# returns column names with vector names
select(df, cols)                              
#>   x  y  z
#> 1 1  6 11
#> 2 2  7 12
#> 3 3  8 13
#> 4 4  9 14
#> 5 5 10 15

Это ошибка или функция?

Ответ 1

IMO он мог бы считаться ошибкой в 0.7.4 и теперь исправлен/более удобен для пользователя.

С переходом к tidyselect, логика стала немного более сложной. Если вы сравниваете dplyr::select_vars с новым tidyselect::vars_select (это варианты, используемые dplyr:select.data.frame в 0.7.4 и 0.7.5 соответственно), вы можете обнаружить, что строка ниже проигрывала имена для названного и цитированного (строки) случая в 0.7.4:

ind_list <- map_if(ind_list, is_character, match_var, table = vars)

# example:
dplyr:::select.data.frame(mtcars, c(a = "mpg", b = "disp"))

Обратите внимание, что это не проблема названных векторов вообще, так как типичный случай без кавычек всегда прекрасен:

dplyr:::select.data.frame(mtcars, c(a = mpg, b = disp))
# (here the names are indeed "a" and "b" afterwards)

Существует строка кода, которая обрабатывает использование c():

ind_list <- map_if(ind_list, !is_helper, eval_tidy, data = names_list)

eval_tidy из пакета rlang, и в приведенной выше строке возвращалось следующее для проблемного вызова:

[[1]]
 a      b 
 "mpg" "disp" 

Теперь с tidyselect у нас есть дополнительная обработка, см. Https://github.com/tidyverse/tidyselect/blob/master/R/vars-select.R.

В частности, vars_select_eval имеет следующую строку, где обрабатывает использование c():

ind_list <- map_if(quos, !is_helper, overscope_eval_next, overscope = overscope)

overscope_eval_next снова из пакета rlang и вызывает ту же процедуру, что и eval_tidy, но он получает вариант перекоса c() который обрабатывает строки (через аргумент overscope). См. tidyselect:vars_c. Поэтому после этой строки случай c(a = "mpg", b = "disp") становится таким же, как c(a = mpg, b = disp):

[[1]]
a b   # these are the names
1 3   # these are the positions of the selected cols

is_character тогда больше не выполняется в следующем коде, в отличие от выше с rlang::eval_tidy.

Если вы посмотрите на эти функции в rlang, то тот факт, что overscope_eval_next мягко устарел в пользу eval_tidy может смутить вас, учитывая приведенное выше. Но здесь я предполагаю, что tidyselect просто не был "очищен" от этого до сих пор (именование несоответствий и т.д. Также нужно было бы решить, так что он переписывает больше, чем только одну строку с вызовом). Но в конце концов, eval_tidy можно использовать таким же образом сейчас и, вероятно, будет.