2

我想创建一个从不同环境更新数据框的函数。具体来说,我想使用该Hmisc::label()函数更新数据框的标签。

assign_label <- function(df, col) {
  col <- rlang::as_name(rlang::ensym(col))
  Hmisc::label(df[,col]) <- fetch_label(col)
}

fetch_label <- function(col) {
  val <- c("mpg" = "MPG",
           "hp" = "HP") 
  unname(val[col])
}

以下代码执行没有问题:assign_label(mtcars, hp)

但是,它实际上并没有改变调用环境中的数据帧。我只是不知道如何让它做我想象的那样。

理想情况下,我希望能够将数据框通过管道传输到此函数:

mtcars %>% assign_label(mpg)

4

2 回答 2

3

1) 返回修改后的对象在 R 中不鼓励就地修改对象。通常的方法是返回数据框,然后将其分配给新名称或返回原始名称来破坏或隐藏它。

assign_label <- function(df, col) {
  col <- deparse(substitute(col))
  Hmisc::label(df[[col]]) <- fetch_label(col)
  df
}

mtcars_labelled <- mtcars %>% assign_label(mpg)

2) magrittr 尽管我们上面已经说过了,但在 R 和一些 R 包中仍有一些可以修改的功能。magrittr 包提供了覆盖或隐藏输入的语法。使用(1)中的定义,我们可以写:

library(mtcars)
mtcars %<>% assign_label(mpg)

如果 mtcars 在全局环境中,它将用新值覆盖它,但在这种情况下,mtcars 在数据集中,因此将新的 mtcars 写入调用者,而数据集中的原始值保持不变。

3) 替换函数虽然没有广泛使用,但 R 确实提供了替换函数,这些函数是这样定义和使用的。这确实会覆盖或隐藏输入。

`assign_label<-` <- function(df, value) {
  Hmisc::label(df[[value]]) <- fetch_label(value)
  df
}

assign_label(mtcars) <- "mpg"

笔记

顺便说一句,如果目标是与 tidyverse 一致的接口,则使用tidyselect检索列名称,以便以下示例起作用:

assign_labels <- function(df, col) {
  nms <- names(select(df, {{col}}))
  for(nm in nms) Hmisc::label(df[[nm]]) <- fetch_label(nm)
  df
}

mtcars_labelled <- mtcars %>% assign_labels(starts_with("mp"))
str(mtcars_labelled)

mtcars_labelled <- mtcars %>% assign_labels(mpg|hp)
str(mtcars_labelled)
于 2020-08-28T23:09:52.153 回答
0

关于不要在函数范围之外修改的评论,我创建了两个函数,它们为新的数据框分配标签。

fetch_label <- function(col) {
  val <- c("mpg" = "MPG",
           "hp" = "HP") 
  unname(val[col])
}
 
assign_label <- function(df, col) {
  col <- rlang::as_name(rlang::ensym(col))
  Hmisc::label(df[[col]]) <- fetch_label(col)
  return(df)
}

assign_labels <- function(df) {
  purrr::iwalk(df, function(.x, .y) {
    lab <- fetch_label(.y)
    Hmisc::label(df[[col]]) <<- lab
  })
  return(df)
}

mtcars <- mtcars %>% assign_label(hp)
mtcars <- mtcars %>% assign_labels()
于 2020-08-28T22:48:36.767 回答