Среды пакетов не работают должным образом в конвейере

#r #environment-variables #pipe

#r #переменные среды #труба

Вопрос:

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

В magrittr конвейере я хочу сохранить их в переменной среды, которую я получу позже.

Однако я сталкиваюсь с проблемой: кажется, что переменные среды не были изменены до конца конвейера.

Вот пример с большинством полезных функций:

 devtools::install_github("DanChaltiel/crosstable", build_vignettes=TRUE)
library(crosstable) #for functions set_label() and get_label() but you can test 
                    #with other label-management packages (Hmisc, expss...)

labels_env = rlang::new_environment()
save_labels = function(.tbl){
    labels_env$last_save = tibble(
        name=names(.tbl),
        label=get_label(.tbl)[.data$name]
    )
    invisible(.tbl)
}
get_last_save = function(){
    labels_env$last_save
}
import_labels = function(.tbl){
    data_label = get_last_save()
    for(i in 1:nrow(data_label)){
        name = as.character(data_label[i, name_from])
        label = as.character(data_label[i, label_from])
        .tbl[name] = set_label(.tbl[name], label)
    }
    .tbl
}
 

Это работает точно так, как задумано, поскольку label for disp будет NULL в противном случае:

 library(dplyr)
library(crosstable)
save_labels(mtcars2)
mtcars2 %>%
  transmute(disp=as.numeric(disp) 1) %>%  #removes the label attribute of disp
  import_labels() %>% #
  crosstable(disp)
#>    .id                 label   variable               value
#> 1 disp Displacement (cu.in.)  Min / Max        72.1 / 473.0
#> 2 disp Displacement (cu.in.)  Med [IQR] 197.3 [121.8;327.0]
#> 3 disp Displacement (cu.in.) Mean (std)       231.7 (123.9)
#> 4 disp Displacement (cu.in.)     N (NA)              32 (0)
 

Создано 2021-01-26 пакетом reprex (версия 0.3.0)

Однако save_labels(mtcars2) возвращает mtcars2 невидимо, поэтому я хотел бы иметь возможность передавать всю последовательность. К сожалению, это выдает ошибку:

 library(dplyr)
library(crosstable)
mtcars2 %>%
  save_labels() %>% 
  transmute(disp=as.numeric(disp) 1) %>%
  import_labels() %>% #
  crosstable(disp)
#> Error in .subset2(x, i, exact = exact): attempt to select less than one element in get1index
 

Создано 2021-01-26 пакетом reprex (версия 0.3.0)

Действительно, при использовании каналов переменная среды еще не установлена, когда мы добираемся до import_labels() . Если я повторно запущу этот код, он не выдаст никакой ошибки, но это будет вводить в заблуждение, поскольку будет ссылаться на предыдущее значение labels_env$last_save .

Мое понимание каналов недостаточно хорошо, чтобы заставить это работать. Более того, похоже, что это специфично для среды пакета, поскольку я не смог воспроизвести это поведение в простом R-скрипте.

Есть ли способ, которым я могу использовать каналы с такой переменной окружения внутри пакета?

Ответ №1:

На самом деле это было вызвано критическим изменением, когда пакет magrittr (который предоставляет каналы) перешел с версии v1.5 на версию v2.0.

Это было объяснено в блоге и на NEWS.md .

Более конкретный воспроизводимый пример можно найти в этом выпуске GitHub.

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

 import_labels = function(.tbl){
    force(.tbl) #force evaluation
    data_label = get_last_save()
    for(i in 1:nrow(data_label)){
        name = as.character(data_label[i, name_from])
        label = as.character(data_label[i, label_from])
        .tbl[name] = set_label(.tbl[name], label)
    }
    .tbl
}