有没有办法阻止所有.vars()从$的右侧返回名称?

有没有办法阻止所有.vars()从$的右侧返回名称?,r,expression,R,Expression,base-R函数all.vars()返回表达式中的所有名称。例如: > all.vars( ~ e == M * c^2 ) [1] "e" "M" "c" 有一个R运算符不适合这样做。在许多不使用rlang等非标准求值和函数的人编写的表达式中,名称将是变量的名称。但是,如果这些表达式包含对$的调用,则右侧的名称将不是变量,而是索引或列名。(我知道巧妙地使用环境和数据屏蔽可以模糊变量和列名之间的区别,但这不是重点。) all.

base-R函数
all.vars()
返回表达式中的所有名称。例如:

> all.vars( ~ e == M * c^2  )
[1] "e" "M" "c"
有一个R运算符不适合这样做。在许多不使用rlang等非标准求值和函数的人编写的表达式中,名称将是变量的名称。但是,如果这些表达式包含对
$
的调用,则右侧的名称将不是变量,而是索引或列名。(我知道巧妙地使用环境和数据屏蔽可以模糊变量和列名之间的区别,但这不是重点。)

all.vars()
没有忽略
$
右侧的选项。有没有类似的函数,或者我必须编写自己的expression walker?基本上,我想要一个传递表达式的函数

a $ b + c $ d
将返回“a”和“c”

请求原因

罗兰,你能建议我解释我为什么想要这个,真是太好了。我经常使用矢量化,因为这是我在非常大的数据集上进行计算时获得足够速度的唯一方法。因此,我的代码中有大量这样的内容:

cond <- ¢ A logical vector of 500,000 elements ¢
v1 ¢ (and v2 etc. ) ¢ <- ¢ Numerical or string vectors of the same length ¢
result_size <- length( cond )
result <- rep( NA, result_size )
result[ cond ] <- f( v1[ cond ], v3[ cond ]
                   , v4[ cond ], v7[ cond ]
                   , v9[ cond ], v10[ cond ] 
                   )
result[ ! cond ] <- g( v2[ ! cond ], v3[ ! cond ]
                     , v4[ ! cond ], v5[ ! cond ]
                     , v6[ ! cond ], v8[ ! cond ]
                     , v10[ ! cond ] 
                     )

你应该解释你为什么需要这个,以及其他形式的非标准评估会发生什么。对于您的实际问题,可能有更好的解决方案

我会很快将
$
替换为
[[

replace_dollar <- function(expr) {
  if (!is.language(expr) || length(expr) == 1L) return(expr)
  if (expr[[1]] == quote(`$`)) {
    expr[[1]] <- quote(`[[`)
    expr[[3]] <- as.character(expr[[3]])
  } else {
    for (i in seq_along(expr)[-1]) 
      expr[[i]] <- replace_dollar(expr[[i]])
  } 
  expr
}

expr <- quote(a $ b + c $ d)
replace_dollar(expr)
# a[["b"]] + c[["d"]]
all.vars(replace_dollar(expr))
#[1] "a" "c"

replace\u-dollar您可以使用
all.vars
选项输出整个结构,包括
$
运算符,并从列表中删除
$
运算符的第二个参数:

    test <- ~a$b+c$d
    all <- all.vars(test,functions = T, unique = F)
    all
    #> [1] "~" "+" "$" "a" "b" "$" "c" "d"
    to_remove <- all[c(F,F,all == "$")]
    to_remove
    #> [1] "b" "d"
    vars <- all.vars(test)
    vars
    #> [1] "a" "b" "c" "d"
    vars[!vars %in% to_remove]
    #> [1] "a" "c"

<sup>Created on 2020-08-25 by the [reprex package](https://reprex.tidyverse.org) (v0.3.0)</sup>
测试[1]“a”“c”
由[reprex软件包]于2020-08-25创建(https://reprex.tidyverse.org)(v0.3.0)
或者作为一种功能:

all.vars.new <- function(e) {
  all <- all.vars(e, functions = T,unique = F)
  cols <- all[c(F,F,all == "$")]
  vars <- all.vars(e)
  vars[!vars %in% cols]
}


all.vars.new您似乎在重新发明轮子。为什么您不能简单地使用分组操作(data.table或dplyr实现)?1)分组操作需要更多的键入,因为需要将我作为参数传递的向量子集(在我的示例中为v1到v10)。请注意“[cond]”和“[!cond]”的重复在“请求原因”之后的第一段代码摘录中。在所有这些重复的下标中很容易出错。或者你能建议一种解决方法吗?2)我的代码是用更传统的语言Python翻译的代码。我希望R看起来尽可能像Python,以便编写Python的人可以检查它。既然Python使用条件,那么R.3也应该如此)从概念上讲,我正在编写一个条件,因此我的代码应该看起来像一个。即使它是由group by实现的,这也是一个实现细节,不需要向阅读代码的人透露。我认为这是我的三个步骤中最重要的一点。4)@Roland,我可能误解了你。你的意思是使用group by而不是vecto吗提升?原因是效率。我的数据中有50万组。通过矢量化处理它需要几秒钟。但我能实现的最快的分组解决方案需要3分钟。这是一个在线模型,我的用户不会等那么久。我总是发现通过分组方式循环数据,无论是在data.table还是Tidyverse中,比矢量化慢得多。@Phil van Kleur,答案解决了你的问题吗?
    test <- ~a$b+c$d
    all <- all.vars(test,functions = T, unique = F)
    all
    #> [1] "~" "+" "$" "a" "b" "$" "c" "d"
    to_remove <- all[c(F,F,all == "$")]
    to_remove
    #> [1] "b" "d"
    vars <- all.vars(test)
    vars
    #> [1] "a" "b" "c" "d"
    vars[!vars %in% to_remove]
    #> [1] "a" "c"

<sup>Created on 2020-08-25 by the [reprex package](https://reprex.tidyverse.org) (v0.3.0)</sup>
all.vars.new <- function(e) {
  all <- all.vars(e, functions = T,unique = F)
  cols <- all[c(F,F,all == "$")]
  vars <- all.vars(e)
  vars[!vars %in% cols]
}