6

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

我知道 tidyverse 函数的使用group_by_mutate_这种“字符串友好”版本正在走向弃用,这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


回答

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

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

多谢 :)

4

2 回答 2

4

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

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

请按照以下步骤解决您的问题:
- 将 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"))
于 2017-11-11T13:20:27.423 回答
1

您可以使用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(); 它允许除符号之外的字符。这可能是一个有点高级的话题。您可以在小插图中找到更详细的说明。

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
于 2017-11-12T00:31:58.027 回答