df
我正在编写一个函数来从给定的 data.frame或环境中提取作为字符串提供的变量env
。最初,我一直使用该eval(parse(text=s), df, env)
结构来做到这一点,但我了解到还有更有效的替代方案。其他选项包括:
eval(str2lang(s), df, env)
eval(str2expression(s), df, env)
eval(call(s)[[1]], df, env)
也可能有一个get
解决方案,但我不知道它是否可以先检查变量是否在其中,df
然后再检查env
变量是否不在。
使用microbenchmark
,似乎call
是最快的:
library(microbenchmark)
x1 = 1
df = data.frame(x2 = 2)
microbenchmark(call = eval(call('x1')[[1]], df),
parse = eval(parse(text='x1'), df),
str2lang = eval(str2lang('x1'), df),
str2exp = eval(str2expression('x1'), df),
check = "identical")
#> Unit: microseconds
#> expr min lq mean median uq max neval cld
#> call 1.128 1.2115 1.60815 1.4585 1.6360 4.659 100 a
#> parse 39.183 39.8705 46.60755 40.2405 42.0415 135.462 100 b
#> str2lang 2.235 2.3570 3.26144 2.5995 2.8925 24.641 100 a
#> str2exp 2.230 2.3200 2.81387 2.4780 2.6970 10.312 100 a
microbenchmark(call = eval(call('x2')[[1]], df),
parse = eval(parse(text='x2'), df),
str2lang = eval(str2lang('x2'), df),
str2exp = eval(str2expression('x2'), df),
check = "identical")
#> Unit: microseconds
#> expr min lq mean median uq max neval cld
#> call 1.124 1.194 1.47770 1.3675 1.5795 9.031 100 a
#> parse 38.254 38.762 40.21497 38.9630 39.3120 116.510 100 b
#> str2lang 2.214 2.304 2.55036 2.3960 2.6530 10.639 100 a
#> str2exp 2.238 2.331 2.50011 2.4210 2.6515 3.619 100 a
Run Code Online (Sandbox Code Playgroud)
由reprex 包(v0.3.0)创建于 2020-04-23
因此,我倾向于使用call
,但我想确保这样做不会产生任何意想不到的后果,而不是使用其他解决方案。换句话说,在什么情况下(在我使用它们的上下文中)这四种方法不会给出相同的答案,导致人们倾向于其中一种而不是其他?
\n
和call
都as.symbol
很好,我认为as.symbol
更好。as.symbol
是明确的 R 术语(as.name
是 S 术语)并且与 一样快call
。调用可以包含任何 R 对象,包括符号和其他调用,而且文档无处不在。由于字符串可能包含标点符号,我们无法确定在哪一点失败str2lang
或str2expression
可能弹出什么错误消息。
检查环境中是否存在变量比检查 data.frames 更安全。
\n深入了解一下它们的主要区别在于它们的类别:
\nl = list(w = as.symbol("x"), x = str2lang("x"), y = str2expression("x"), z = call("x"))\nrbind(sapply(l, class), sapply(l, typeof), sapply(l, is.language))\n w x y z \n[1,] "name" "name" "expression" "call" \n[2,] "symbol" "symbol" "expression" "language"\n[3,] "TRUE" "TRUE" "TRUE" "TRUE" \n
Run Code Online (Sandbox Code Playgroud)\n一个很好的起点是R 语言定义,例如 Hadley Wickham 的Advanced R(源代码)中的表达式一章,然后阅读?call
, ?parse
, rlang::call2
。
在评估 R 语言的设计(源代码)中,给出了以下定义:
\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\ xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\ xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0调用参数为可以用符号命名的表达式姓名。
从?call
文档中:
\n\n相反
\nas.call(<string>)
,请考虑使用str2lang(*)
which 是 的高效版本parse(text=*)
。call()
和as.call()
,比parse()
基于方法更可取。
使用pryr::show_c_source(.Internal(str2lang(s)))
我们可以看到str2lang
,str2expression
两者都调用C 函数 do_str2lang
,但参数不同。这种差异可以在以下位置找到?parse
:
str2expression
, 等于parse(text = "x", keep.source = F)
始终是一个表达式。str2lang("x")
、等于parse(text = "x", keep.source = F)[[1]]
可以计算为调用或符号、NULL
数字、整数或逻辑,即调用或更简单的。在R 语言定义中提到,表达式仅在传递给 时才进行求值eval
,其他语言对象可能在某些意外情况下进行求值。这些案例是什么,我找不到。
我们可以在所有方法中抛出一些边缘情况,看看哪些方法可能会失败:
\n"\\""
读取数据时错误解析引用?" "
空变量?变量名中有空格?"_"
非法令牌(遗留赋值运算符)问题?# if it breaks, show how it breaks\ndo <- function(x, ...) tryCatch(eval(x, ..1, ..2), error = function(t) t$message)\ncheck <- function(x, ...){\n list(\n do(call(x)[[1]], ...),\n do(as.symbol(x), ...),\n do(str2lang(x), ...),\n do(str2expression(x), ...)\n )\n}\n\n# test 1: some variables do not exist\ne <- new.env() ; e$x1 <- 5\ndf = data.frame(x2 = 3)\nno_var <- lapply(list("x1", "x2", "\\"", " ", "_", "`", NA_character_), check, df, e)\n\n# test 2: some variables exist\ne <- new.env() ; e$x1 = 5; e$`_` = 2 ; e$"\\"" = 5; e$`NA` <- 5\ndf = data.frame(x2 = 3, " " = 7, "_" = 2)\nvar <- lapply(list("x1", "x2", "\\"", " ", "_", "`", NA_character_), check, df, e)\n\n# difference in outcomes in\nvar[3:7]\nno_var[3:7]\n
Run Code Online (Sandbox Code Playgroud)\n当变量不存在时:\nas.symbol
并且call
始终到达eval
,str2lang
并且str2expression
在所有情况下都会提前失败。与给出空字符串时str2expression
不同。str2lang
当变量存在时:\nas.symbol
并call
在情况 3、4、7 中成功,同时str2lang
抛出str2expression
错误。
顺便说一句,在 data.frames 中存储变量时要小心,它们很容易损坏。
\nnames(data.frame(" " = 1, "_" = 2, "\\"" = 3, "`" = 4))\n[1] "X." "X_" "X..1" "X..2"\n
Run Code Online (Sandbox Code Playgroud)\n