每行评估字符串中不同的逻辑条件

8

我有一个像这样的data.frame:

  value     condition
1  0.46   value > 0.5
2  0.96 value == 0.79
3  0.45 value <= 0.65
4  0.68 value == 0.88
5  0.57   value < 0.9
6  0.10  value > 0.01
7  0.90  value >= 0.6
8  0.25  value < 0.91
9  0.04   value > 0.2

structure(list(value = c(0.46, 0.96, 0.45, 0.68, 0.57, 0.1, 0.9, 
0.25, 0.04), condition = c("value > 0.5", "value == 0.79", "value <= 0.65", 
"value == 0.88", "value < 0.9", "value > 0.01", "value >= 0.6", 
"value < 0.91", "value > 0.2")), class = "data.frame", row.names = c(NA, 
-9L))

我希望能够针对每行的condition列中的字符串进行评估。

因此,结果会像这样。

  value     condition  goal
1  0.46   value > 0.5 FALSE
2  0.96 value == 0.79 FALSE
3  0.45 value <= 0.65  TRUE
4  0.68 value == 0.88 FALSE
5  0.57   value < 0.9  TRUE
6  0.10  value > 0.01  TRUE
7  0.90  value >= 0.6  TRUE
8  0.25  value < 0.91  TRUE
9  0.04   value > 0.2 FALSE

我认为在 dplyr 框架内有一个方便的 NSE 解决方案。我尝试过使用 !!expr() 等方法进行实验。当我尝试使用 condition 进行子集筛选时,我得到了一些有希望的结果。

result <- df[0,]
for(i in 1:nrow(df)) { 
  result <- rbind(result, filter_(df[i,], bquote(.(df$condition[i]))))
}

但我不喜欢这个解决方案,也不完全符合我的要求。

我希望有人能帮忙。

更新:我正在尝试避免使用eval(parse(..))

4个回答

5
一种简单易行的解决方案是使用 eval(parse...。该方法可以解析字符串并将其转换为可执行代码。
library(dplyr)

df %>%
  rowwise() %>%
  mutate(goal = eval(parse(text = condition)))

# A tibble: 9 x 3
#  value condition     goal 
#  <dbl> <chr>         <lgl>
#1 0.46  value > 0.5   FALSE
#2 0.96  value == 0.79 FALSE
#3 0.45  value <= 0.65 TRUE 
#4 0.68  value == 0.88 FALSE
#5 0.570 value < 0.9   TRUE 
#6 0.1   value > 0.01  TRUE 
#7 0.9   value >= 0.6  TRUE 
#8 0.25  value < 0.91  TRUE 
#9 0.04  value > 0.2   FALSE

不过,在使用它之前我建议阅读一些帖子


1
当然是这样,我知道eval(parse(..)),但我试图在dplyr中使用NSE找到一个不需要它的解决方案。我应该更加具体化。 - Humpelstielzchen

5

不完全确定你是否在寻找这样的东西,但是你也可以使用 lazyeval 中的 lazy_eval()

df %>%
 rowwise() %>%
 mutate(res = lazy_eval(sub("value", value, condition)))

  value condition     res  
  <dbl> <chr>         <lgl>
1 0.46  value > 0.5   FALSE
2 0.96  value == 0.79 FALSE
3 0.45  value <= 0.65 TRUE 
4 0.68  value == 0.88 FALSE
5 0.570 value < 0.9   TRUE 
6 0.1   value > 0.01  TRUE 
7 0.9   value >= 0.6  TRUE 
8 0.25  value < 0.91  TRUE 
9 0.04  value > 0.2   FALSE

尽管与eval(parse(...))非常接近,但也可以使用rlang中的parse_expr()来实现:

df %>%
 rowwise() %>%
 mutate(res = eval(rlang::parse_expr(condition)))

3

使用 match.fun

# get function, and the value
myFun <- lapply(strsplit(df1$condition, " "), function(i){
  list(f = match.fun(i[ 2 ]), 
       v = as.numeric(i[ 3 ]))
})

df1$goal <- mapply(function(x, y){ 
  x[[ "f" ]](y, x[ "v" ])
  }, x = myFun, y = df1$value)

#   value     condition  goal
# 1  0.46   value > 0.5 FALSE
# 2  0.96 value == 0.79 FALSE
# 3  0.45 value <= 0.65  TRUE
# 4  0.68 value == 0.88 FALSE
# 5  0.57   value < 0.9  TRUE
# 6  0.10  value > 0.01  TRUE
# 7  0.90  value >= 0.6  TRUE
# 8  0.25  value < 0.91  TRUE
# 9  0.04   value > 0.2 FALSE

2

如果你想避免使用eval(parse...,你可以尝试这个方法:

最初的回答:

library(tidyverse)
df %>% mutate(bound = as.numeric(str_extract(condition, "[0-9 \\.]*$")),
              goal = case_when(grepl("==", condition) ~ value == bound,
                               grepl(">=", condition) ~ value >= bound,
                               grepl("<=", condition) ~ value <= bound,
                               grepl(">", condition) ~ value > bound,
                               grepl("<", condition) ~ value < bound,
                               T ~ NA))

  value     condition bound  goal
1  0.46   value > 0.5  0.50 FALSE
2  0.96 value == 0.79  0.79 FALSE
3  0.45 value <= 0.65  0.65  TRUE
4  0.68 value == 0.88  0.88 FALSE
5  0.57   value < 0.9  0.90  TRUE
6  0.10  value > 0.01  0.01  TRUE
7  0.90  value >= 0.6  0.60  TRUE
8  0.25  value < 0.91  0.91  TRUE
9  0.04   value > 0.2  0.20 FALSE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接