在 R 中的函数中创建和使用新变量:tidyverse 中的 NSE 编程错误

Posted

技术标签:

【中文标题】在 R 中的函数中创建和使用新变量:tidyverse 中的 NSE 编程错误【英文标题】:Creating and using new variables in function in R: NSE programing error in the tidyverse 【发布时间】:2018-04-23 20:32:29 【问题描述】:

在阅读和重新阅读许多“使用 dplyr 编程”指南之后,我仍然找不到解决我的特殊情况的方法。

我知道使用 group_by_mutate_ 和这种“字符串友好”版本的 tidyverse 函数正在走向弃用,而 enquo 是要走的路。

但是,我的情况有些不同,我正在努力寻找一种整洁的方式来解决它。

确实,我的目标是在函数中创建和操作数据框。根据其他变量创建(mutating)新变量,使用它们等。

但是,无论我多么努力,我的代码要么出错,要么在检查包时返回一些警告,例如no visible binding for global variable ...

这是一个可重现的示例:

这是我想做的:

df <- data.frame(X=c("A", "B", "C", "D", "E"),
                 Y=c(1, 2, 3, 1, 1))
new_df <- df %>%
  group_by(Y) %>%
  summarise(N=n()) %>%
  mutate(Y=factor(Y, levels=1:5)) %>%
  complete(Y, fill=list(N = 0)) %>%
  arrange(Y) %>%
  rename(newY=Y) %>%
  mutate(Y=as.integer(newY))

一些常见的 dplyr 操作,预期结果应该是:

# A tibble: 5 x 3
     newY     N     Y
<fctr> <dbl> <int>
1      1     3     1
2      2     1     2
3      3     1     3
4      4     0     4
5      5     0     5

我希望这段代码能够在内部一个函数中安静地工作。以下是我处理非 NSE 问题的最佳尝试:

myfunction <- function()
  df <- data.frame(X=c("A", "B", "C", "D", "E"),
                   Y=c(1, 2, 3, 1, 1))
  new_df <- df %>%
    group_by_("Y") %>%
    summarise(!!"N":=n()) %>%
    mutate(!!"Y":=factor(Y, levels=1:5)) %>%
    complete_("Y", fill=list(N = 0)) %>%
    arrange_("Y") %>%
    rename(!!"newY":="Y") %>%
    mutate(!!"Y":=as.integer(newY))

很遗憾,我仍然收到以下消息:

myfunction: no visible global function definition for ':='
myfunction: no visible binding for global variable 'Y'
myfunction: no visible binding for global variable 'newY'
Undefined global functions or variables:
  := Y n.Factors n_optimal newY

有办法解决吗?非常感谢!

编辑:我正在使用 R 3.4.1、dplyr_0.7.4、tidyr_0.7.2 和 tidyverse_1.1.1


回答

感谢我设法解决的 cmets,这是可行的解决方案:

myfunction <- function()
  df <- data.frame(X=c("A", "B", "C", "D", "E"),
                   Y=c(1, 2, 3, 1, 1))
  new_df <- df %>%
    group_by_("Y") %>%
    summarise_("N"=~n()) %>%
    mutate_("Y"= ~factor(Y, levels=1:5)) %>%
    complete_("Y", fill=list(N = 0)) %>%
    arrange_("Y") %>%
    rename_("newY"=~Y) %>%
    mutate_("Y"=~as.integer(newY))

非常感谢:)

【问题讨论】:

您使用的是哪个版本的dplyr?你加载的是library(tyidyverse) 还是library(rlang)?运行代码时,我没有遇到与您相同的错误。分享来自sessionInfo() 的信息,说明您正在运行哪些软件包和版本。在你的新函数中究竟需要参数化什么? 我确实在开始时加载了 tidyverse(我已经编辑了我的问题以添加版本)。 “什么需要参数化”是什么意思? 现在你的函数似乎根本不需要任何花哨的 NSE 东西。所以您希望能够将“Y”和“N”以及变量传递给函数或其他东西?您使用字符串的原因是什么?到底要怎么调用函数? 好的,所以我正在开发一个从分析中提取一些值的函数,将其转换为基本数据框(我的示例中的 df)。然后,我执行这些确切的步骤(如示例所示)以获得一个新的数据框。后来,我将这个数据框重新用于其他计算、绘图和其他东西。我的意思是函数有效,但困扰我的是我在构建包时收到的警告,因为它阻止了 CRAN 发布:( 哦,这实际上是关于在 R 包中正确使用 rlang。问题中似乎缺少这种背景。见相关:here1here2here3。在编写自己的包时避免这些东西要容易得多。 【参考方案1】:

答案不在“使用 dplyr 编程”指南中,因为您的问题更为普遍。尽管您的代码处理非标准评估,但您的案例不需要它。如果您删除处理非标准评估的代码,您将减少需要修复的问题数量。

不过,一些重要的问题仍然存在——NAMESPACE 问题。每当您在自己的包的函数中使用来自其他包的函数时,您都会处理 NAMESPACE。 NAMESPACE 不是一个简单的话题,但如果你正在编写包,学习一点是值得的。我建议您阅读:从 r-pkgs.had.co.nz/namespace.html,找到“Imports”部分并阅读它的介绍以及副标题“R 函数”。这将帮助您理解我在下面发布的步骤、代码和 cmets。

请按照以下步骤解决您的问题: - 将 dplyr、magrittr 和 tidyr 添加到DESCRIPTION。 - 引用函数为PACKAGE::FUNCTION(). - 删除所有 !!:=,因为在这种情况下您不需要它们。 - 从 magrittr 导入和导出管道。 - 从 rlang 导入 .data。 - 将全局变量传递给 utils::globalVariables()。 - 重建,重新加载,重新检查。

# I make your function shorter to focus on the important details.
myfunction <- function()
  df <- data.frame(
    X = c("A", "B", "C", "D", "E"),
    Y = c(1, 2, 3, 1, 1)
  )
   df %>%
     dplyr::group_by(.data$Y) %>%
     dplyr::summarise(N = n())


# Fix check() notes

#' @importFrom magrittr %>%
#' @export
magrittr::`%>%`

#' @importFrom rlang .data
NULL

utils::globalVariables(c(".data", "n"))

【讨论】:

虽然代码 cmets 是提供代码解释的好方法,但除了代码之外,请提供一些关于您的答案的解释。 请在答案中添加解释。 感谢 Mehraban 帮助我改进答案!【参考方案2】:

您可以使用rlang::sym()(或base::as.name())将字符转换为符号,所以让我添加一个替代答案。

请注意,我并不是要强迫您丢弃这些已弃用的功能。你可以使用对你来说容易理解的。 (不过我相信sym() 更有用)

案例一:rlang::sym()的基本用法

此代码

group_by_("Y") %>%

可以写成

group_by(!! rlang::sym("Y"))

或者您甚至可以预先将符号分配给变量。

col_Y <- rlang::sym("Y")
df %>%
  group_by(!! col_Y)

案例 2:左侧符号

这段代码完全没问题。

summarise(!!"N":=n())

LHS 允许使用字符和符号。所以这也很好:

col_N <- rlang::sym("N")
# ...
  summarise(!! col_N := n())

案例 3) 选择语义

select()rename()mutate() 等其他函数具有不同的语义;它允许除符号之外的字符。这可能是一个有点高级的话题。你可以在a vignette找到更详细的解释。

More precisely, the code bellow are both permitted:

rename(new = old)
rename(new = "old")

So, this code is fine.

rename(!! "newY" := "Y")

(示例)

reprex::reprex_info()
#> Created by the reprex package v0.1.1.9000 on 2017-11-12

library(dplyr, warn.conflicts = FALSE)
library(tidyr)

df <- data.frame(X=c("A", "B", "C", "D", "E"),
                 Y=c(1, 2, 3, 1, 1))

col_Y <- rlang::sym("Y")
col_N <- rlang::sym("N")
col_newY <- rlang::sym("newY")

df %>%
  group_by(!! col_Y) %>%
  summarise(!! col_N := n()) %>%
  mutate(!! col_Y := factor(!! col_Y, levels=1:5)) %>%
  complete(!! col_Y, fill = list(N = 0)) %>%
  arrange(!! col_Y) %>%
  rename(!! col_newY := !! col_Y) %>%
  mutate(!! col_Y := as.integer(!! col_newY))
#> # A tibble: 5 x 3
#>     newY     N     Y
#>   <fctr> <dbl> <int>
#> 1      1     3     1
#> 2      2     1     2
#> 3      3     1     3
#> 4      4     0     4
#> 5      5     0     5

【讨论】:

以上是关于在 R 中的函数中创建和使用新变量:tidyverse 中的 NSE 编程错误的主要内容,如果未能解决你的问题,请参考以下文章

在java中创建和初始化对象

如何在 Protractor 中创建和操作 Promise?

markdown 在wordpress中创建和使用自定义全局变量。

如何在 Angular CLI 中创建和使用新的构建配置?

如何在 NMake 中创建和使用多行字符串变量?

在 R 中创建和组合两个图 - xy 线图和条形链图