将 data.table 中的变量转换为公式
Convert variables in data.table to formula
我有一个示例 data.table data
如下:
VarName Formulae
1: A 1+1
2: B A+3
3: C B*10
4: D A+C
5: E D/2
我想将 Formulae
列转换成公式,这样输出就可以变成这样:
VarName Result
1: A 2
2: B 5
3: C 50
4: D 52
5: E 26
基本上VarName
栏是变量名,Formulae
栏是对应的公式。
A = 1+1
B = A+3
C = B*10
D = A+C
E = D/2
我已经尝试使用 eval
和 parse
函数,例如 data$VarName = eval(parse(text = "data$Formulae"))
,但是我无法获得所需的输出。
使用apply
:
df <- data.frame("VarName"=c("X","Y"),"Formulae"=c("1+1","X+1"))
df$formulas <- apply(df,1,function(x)eval(parse(text = paste0(x["VarName"]," ~ ",x["Formulae"]))))
使用 eval(parse(...))
结构是正确的,但这应该可以正常工作。但是,也许有人会回答更简洁的建议。
请注意,“公式”列不能是向量,因此它是一个列表。
str(df)
'data.frame': 2 obs. of 3 variables:
$ VarName : chr "X" "Y"
$ Formulae: chr "1+1" "X+1"
$ formulas:List of 2
..$ :Class 'formula' language X ~ 1 + 1
.. .. ..- attr(*, ".Environment")=<environment: 0x000002933f8904a8>
..$ :Class 'formula' language Y ~ X + 1
.. .. ..- attr(*, ".Environment")=<environment: 0x000002933fb6f3b8>
这可能会导致数据帧使用中出现一些问题。在这种情况下,我建议使用像 purrr
这样的映射工具,而不是将所有内容连接到一个数据框中。
遍历 VarName 用括号内的 Formulae 替换它们,然后计算:
res <- setNames(x$Formulae, x$VarName)
while(any(grepl(paste0(names(res), collapse = "|"), res))) {
for(i in names(res)){
res <- gsub(i, paste0("(", res[ i ], ")"), res, fixed = TRUE)
}
}
#res, after replacements:
# A B
# "1+1" "(1+1)+3"
# C D
# "((1+1)+3)*10" "(1+1)+(((1+1)+3)*10)"
# E
# "((1+1)+(((1+1)+3)*10))/2"
# evaluate
sapply(res, function(i) eval(parse(text = i)))
#A B C D E
#2 5 50 52 26
一种方法是将 Formulae
转换为实际的 one-sided 公式,然后在 lst()
中依次求值的函数允许顺序构建对象。这依赖于 tidyverse
框架的元编程功能,而不是 data.table
.
library(dplyr)
library(purrr)
df <- data.frame(VarName = LETTERS[1:5],
Formulae = c("1+1", "A+3", "B*10", "A+C", "D/2"))
lst(!!!map(set_names(df$Formulae, df$VarName),
~ quo(
as_mapper(reformulate(.x))()
)))
$A
[1] 2
$B
[1] 5
$C
[1] 50
$D
[1] 52
$E
[1] 26
或者:
lst(!!!set_names(df$Formulae, df$VarName) %>% map(str2lang))
如以下评论中所述,这些要求公式按顺序排列。
看到此任务的另一个函数很有趣,它在更复杂(未指定求值顺序)的情况下很有用 -- delayedAssign
为名称分配一个值,并且仅在以下情况下对其求值要求。这样,每个对象都会按顺序求值,直到达到其值。例如,考虑以下“data.frame”:
d = structure(list(v = c("a", "b", "A", "B", "C", "D", "E"),
f = c("C+b", "A+B/D", "1+1", "A+3", "B*10", "A+C", "D/2")),
class = "data.frame", row.names = c(NA, -7L))
然后我们设置一个新环境(以避免混乱.GlobalEnv
)并分配我们的变量:
e = new.env()
forms = parse(text = d$f)
for(i in 1:nrow(d)) do.call(delayedAssign, list(d$v[i], forms[[i]], e, e))
并评估:
unlist(mget(ls(e), e)) #or
unlist(eapply(e, eval))
# A B C D a E b
# 2.000000 5.000000 50.000000 52.000000 52.096154 26.000000 2.096154
我有一个示例 data.table data
如下:
VarName Formulae
1: A 1+1
2: B A+3
3: C B*10
4: D A+C
5: E D/2
我想将 Formulae
列转换成公式,这样输出就可以变成这样:
VarName Result
1: A 2
2: B 5
3: C 50
4: D 52
5: E 26
基本上VarName
栏是变量名,Formulae
栏是对应的公式。
A = 1+1
B = A+3
C = B*10
D = A+C
E = D/2
我已经尝试使用 eval
和 parse
函数,例如 data$VarName = eval(parse(text = "data$Formulae"))
,但是我无法获得所需的输出。
使用apply
:
df <- data.frame("VarName"=c("X","Y"),"Formulae"=c("1+1","X+1"))
df$formulas <- apply(df,1,function(x)eval(parse(text = paste0(x["VarName"]," ~ ",x["Formulae"]))))
使用 eval(parse(...))
结构是正确的,但这应该可以正常工作。但是,也许有人会回答更简洁的建议。
请注意,“公式”列不能是向量,因此它是一个列表。
str(df)
'data.frame': 2 obs. of 3 variables:
$ VarName : chr "X" "Y"
$ Formulae: chr "1+1" "X+1"
$ formulas:List of 2
..$ :Class 'formula' language X ~ 1 + 1
.. .. ..- attr(*, ".Environment")=<environment: 0x000002933f8904a8>
..$ :Class 'formula' language Y ~ X + 1
.. .. ..- attr(*, ".Environment")=<environment: 0x000002933fb6f3b8>
这可能会导致数据帧使用中出现一些问题。在这种情况下,我建议使用像 purrr
这样的映射工具,而不是将所有内容连接到一个数据框中。
遍历 VarName 用括号内的 Formulae 替换它们,然后计算:
res <- setNames(x$Formulae, x$VarName)
while(any(grepl(paste0(names(res), collapse = "|"), res))) {
for(i in names(res)){
res <- gsub(i, paste0("(", res[ i ], ")"), res, fixed = TRUE)
}
}
#res, after replacements:
# A B
# "1+1" "(1+1)+3"
# C D
# "((1+1)+3)*10" "(1+1)+(((1+1)+3)*10)"
# E
# "((1+1)+(((1+1)+3)*10))/2"
# evaluate
sapply(res, function(i) eval(parse(text = i)))
#A B C D E
#2 5 50 52 26
一种方法是将 Formulae
转换为实际的 one-sided 公式,然后在 lst()
中依次求值的函数允许顺序构建对象。这依赖于 tidyverse
框架的元编程功能,而不是 data.table
.
library(dplyr)
library(purrr)
df <- data.frame(VarName = LETTERS[1:5],
Formulae = c("1+1", "A+3", "B*10", "A+C", "D/2"))
lst(!!!map(set_names(df$Formulae, df$VarName),
~ quo(
as_mapper(reformulate(.x))()
)))
$A
[1] 2
$B
[1] 5
$C
[1] 50
$D
[1] 52
$E
[1] 26
或者:
lst(!!!set_names(df$Formulae, df$VarName) %>% map(str2lang))
如以下评论中所述,这些要求公式按顺序排列。
看到此任务的另一个函数很有趣,它在更复杂(未指定求值顺序)的情况下很有用 -- delayedAssign
为名称分配一个值,并且仅在以下情况下对其求值要求。这样,每个对象都会按顺序求值,直到达到其值。例如,考虑以下“data.frame”:
d = structure(list(v = c("a", "b", "A", "B", "C", "D", "E"),
f = c("C+b", "A+B/D", "1+1", "A+3", "B*10", "A+C", "D/2")),
class = "data.frame", row.names = c(NA, -7L))
然后我们设置一个新环境(以避免混乱.GlobalEnv
)并分配我们的变量:
e = new.env()
forms = parse(text = d$f)
for(i in 1:nrow(d)) do.call(delayedAssign, list(d$v[i], forms[[i]], e, e))
并评估:
unlist(mget(ls(e), e)) #or
unlist(eapply(e, eval))
# A B C D a E b
# 2.000000 5.000000 50.000000 52.000000 52.096154 26.000000 2.096154