包环境在管道中未按预期工作

Package environments are not working as expected in a pipeline

在我正在处理的包中,我使用环境来保存和检索数据框的标签。

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
}

这完全符合预期,因为 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)

reprex package (v0.3.0)

于 2021-01-26 创建

但是,save_labels(mtcars2) returns 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

reprex package (v0.3.0)

于 2021-01-26 创建

确实,当我们使用管道时,当我们到达import_labels()时,环境变量还没有设置。如果我重新 运行 这段代码,它不会抛出任何错误,但这会产生误导,因为它会引用 labels_env$last_save.

的先前值

我对管道的理解不够好,无法使它正常工作。此外,它似乎特定于包环境,因为我无法在普通 R 脚本中重现此行为。

有没有一种方法可以在包中使用带有这种环境变量的管道?

这实际上是由于软件包 magrittr(提供管道)从 v1.5 升级到 v2.0 时发生的重大更改造成的。

这已在 the blog and on NEWS.md 上进行了解释。

可以在 this GitHub issue.

上找到更具体的可重现示例

在新的 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
}