R:Tidyverse 选择语义 tidyselect::eval_select 将数字附加到重复项

Cla*_*eri 5 r dplyr tidyverse rlang tidyselect

我试图了解 tidyverse 设计以及如何使用它进行编程有一段时间了。我试图编写一个使用 tidyselect 语义的函数,我发现tidyselect::eval_select将数字附加到 lhs 表达式。看到这个语义用于列重命名,这并不奇怪。不幸的是,我用于构建数据结构的函数不需要这种行为,它需要表达式的 lhs 中提供的常规名称(根据需要重复多次)。我还没有设法找出这种行为的来源;它似乎是一个,make.unique但我找不到它的实现位置。如果你知道,我很想学习,如果没有,解决我的问题不应该依赖于它。我想要的只是 lhs 名称没有附加数字,如示例所示:

library(tidyverse)

# Data
data <- mtcars[, 8:11]

# Example
data %>%
  tidyselect::eval_select(rlang::expr(c(foo = 1, bar = c(2:4), foobar = c(1, "am", "gear", "carb"))), .)
#>     foo    bar1    bar2    bar3 foobar1 foobar2 foobar3 foobar4 
#>       1       2       3       4       1       2       3       4

# Function
test <- function(.data, ...) {
  loc <- tidyselect::eval_select(rlang::expr(c(...)), .data)
  names <- names(.data)
  list(names(loc), names[loc])
}

data %>%
  test(foo = 1, bar = c(2:4), foobar = c(1, "am", "gear", "carb"))
#> [[1]]
#> [1] "foo"     "bar1"    "bar2"    "bar3"    "foobar1" "foobar2" "foobar3"
#> [8] "foobar4"
#> 
#> [[2]]
#> [1] "vs"   "am"   "gear" "carb" "vs"   "am"   "gear" "carb"
Run Code Online (Sandbox Code Playgroud)

reprex 包( v2.0.0 )于 2021 年 5 月 22 日创建

期望的输出:

#> [[1]]
#> [1] "foo"     "bar"    "bar"    "bar"    "foobar" "foobar" "foobar"
#> [8] "foobar"
#> 
#> [[2]]
#> [1] "vs"   "am"   "gear" "carb" "vs"   "am"   "gear" "carb"
Run Code Online (Sandbox Code Playgroud)

任何帮助是极大的赞赏。

Tim*_*Fan 2

ensure_named该问题是由一个深深嵌套在s 实现中的函数引起的eval_select。它是vars_select_eval函数的一部分。

ensure_named(pos, vars, uniquely_named, allow_rename)

好消息是,我们只需要覆盖该uniquely_named参数,并且该参数是从第一个被调用的实现函数开始的,eval_select_impl该函数是由eval_select它自己调用的。所以我们需要做的就是重写tidyselect::eval_select

为了获得想要的输出,我们需要做两件事:

  1. 添加为参数并在调用函数时uniquely_named = NULL指定FALSE
  2. 指定现有参数name_spec = "{outer}"uniquely_named除非设置为,否则仅执行此步骤是不够的FALSE

在实际代码之前,请注意:

tidyselect::eval_select故意不允许重复的列名。

对于初学者来说,不可能轻松创建tibble具有重复列名的列:

tibble(a = 1:3, b = 4:6, a = 7:9)
#> Error: Column name `a` must not be duplicated.
#> Use .name_repair to specify repair.
Run Code Online (Sandbox Code Playgroud)

一种解决方法是使用包含以下内容的列表tibble::new_tibble

tibble::new_tibble(list(a = 1:3, b = 4:6, a = 7:9), nrow = 3)
#> # A tibble: 3 x 3
#>       a     b     a
#>   <int> <int> <int>
#> 1     1     4     7
#> 2     2     5     8
#> 3     3     6     9
Run Code Online (Sandbox Code Playgroud)

对于 a,只有当参数设置为data.frame时,才可以创建非唯一名称:check.namesFALSE

data.frame(a = 1:3, b = 4:6, a = 7:9, check.names = FALSE)
#>   a b a
#> 1 1 4 7
#> 2 2 5 8
#> 3 3 6 9
Run Code Online (Sandbox Code Playgroud)

但是当我们将它data.frame与常规的 {dplyr} 动词一起使用时,将会抛出一个错误,告诉我们不能转换具有重复名称的数据帧:

tibble(a = 1:3, b = 4:6, a = 7:9)
#> Error: Column name `a` must not be duplicated.
#> Use .name_repair to specify repair.
Run Code Online (Sandbox Code Playgroud)

因此,我们可以假设,不建议data.frame在 {tidyverse} 中使用具有重复名称的 s。它可能与整洁数据的概念相矛盾。

话虽这么说,下面是上述解决这个问题的方法:

tibble::new_tibble(list(a = 1:3, b = 4:6, a = 7:9), nrow = 3)
#> # A tibble: 3 x 3
#>       a     b     a
#>   <int> <int> <int>
#> 1     1     4     7
#> 2     2     5     8
#> 3     3     6     9
Run Code Online (Sandbox Code Playgroud)

由reprex 包(v0.3.0)于 2021-05-22 创建

  • 是的,“不整洁”的数据结构将是内部函数的一部分,但主函数将仅提供整洁的输出。再次感谢您的有用回复和指导。 (2认同)