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

时间:2017-11-10 15:05:18

标签: r dplyr tidyverse mutate nse

阅读并重新阅读使用dplyr"编写的许多"编程指南,我仍然无法找到解决我的具体案例的方法。

我理解使用group_by_mutate_和#34;字符串友好" tidyverse函数的版本正朝着弃用方向发展,enquo是可行的方法。

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

实际上,我的目标是在函数中创建和操作数据帧。根据其他人创建(变异)新变量,使用它们等等。

但是,无论我怎么努力,我的代码都会出错或在包检查时返回一些警告,例如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

ANSWER

感谢我已经设法解决的问题,以下是工作解决方案:

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))
}

非常感谢:)

2 个答案:

答案 0 :(得分:3)

答案并非在&#34;使用dplyr进行编程&#34;指南,因为您的问题更为一般。虽然您的代码处理非标准评估,但您的案例并不需要它。如果删除处理非标准评估的代码,则可以减少需要修复的问题数量。

仍然存在一些重要问题--NAMESPACE的问题。只要您在自己的包中使用其他包中的函数,就可以处理NAMESPACE。 NAMESPACE不是一个简单的话题,但是如果你正在编写软件包,那么学习它会有所收获。我建议你阅读:从r-pkgs.had.co.nz/namespace.html,找到&#34; Imports&#34;并阅读其介绍以及副标题&#34; R功能&#34;。这将有助于您了解我在下面发布的步骤,代码和注释。

按照以下步骤解决问题:
     - 将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"))

答案 1 :(得分:1)

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

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

案例1: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