@@ Lionel Henry谢谢!我对您的示例确实有后续问题。
我正在尝试编写一个函数,该函数采用两个列名以及每个列名的上下边界,因此我可以使用自己选择的列名和边界对数据进行子集化。以mtcars为例,如果我想说我只希望cyl> 4和mpg> 15的行作为数据的子集,在这种情况下,我的函数将使用两个列名称cyl和mpg,每列还有两个下边界名称,分别是4和15。当然,在函数中,我可以选择为其分配一个上限,以将列名(变量)保持在一定范围内。因此,我想出了类似下面的内容,该函数接受您选择的两个变量名称以及每个变量的上限和/或下限。如果我仅对此变量给出一个上边界或下边界,那么它将给我小于或大于该边界的任何东西,如果我给函数同时指定上边界和下边界,则它会返回属于该范围的行。
comb_function<-function(df,var1,var2,var1_lower=NULL,var1_upper=NULL,var2_upper=NULL,var2_lower=NULL){
var1<-enexpr(var1)
var2<-enexpr(var2)
#####for var2,if upper boundary are given by user,do this#####{
filter1<-expr(`$`(df,!!var2))<=var2_upper
#for var1, if upper boundary are given by user,do this# {
filter2<-expr(`$`(df,!!var1))<=var1_upper}
#for var 1,if lower boundary are given by user, do this#{
filter2<-expr(`$`(df,!!var1))>=var1_lower}
#for var1, if both are given by user, do this#{
filter2<-expr(`$`(df,!!var1))>=var1_lower&expr(`$`(df,!!var1))<=var1_upper}
}
#####for var2,if lower boundary are given by user,do this#####{
filter1<-expr(`$`(df,!!var2))>=var2_lower
#for var1,if upper boundary are given by user,do this#{
filter2<-expr(`$`(df,!!var1))<=var1_upper}
#for var1,if lower boundary are given by user,do this#{
filter2<-expr(`$`(df,!!var1))>=var1_lower}
#if both are given by the user,do this{
filter2<-expr(`$`(df,!!var1))>=var1_lower&expr(`$`(df,!!var1))<=var1_upper}
}
#####for var2,if both are given by user,do this#####{
filter1<-expr(`$`(df,!!var2))<=var2_upper&expr(`$`(df,!!var2))>=var2_lower
#for var1,if upper boundary are given by user,do this#{
filter2<-expr(`$`(df,!!var1))<=var1_upper}
#for var1,if lower boundary are given by user,do this#{
filter2<-expr(`$`(df,!!var1))>=var1_lower}
#if both are given by user, do this#{
filter2<-expr(`$`(df,!!var1))>=var1_lower&expr(`$`(df,!!var1))<=var1_upper}
}
output<-df%>%filter(filter1,filter2)%>%summarise(count=n(),avgcyl=mean(cyl,na.rm=TRUE))
return(output)
}
当我以mtcars为例调用此函数时
final1<-comb_function(df=mtcars,var1=mpg,var2=cyl,var1_lower =15,var2_lower=4,var2_upper=6)
我在final1中得到了0个计数和avgcrl的NaN。因此,当filter()
评估()
内的内容时,只会得到FALSE,否TRUE,我认为这就是为什么没有行返回的原因。
我有一个理论为什么会这样。如果我这样做:
x<-expr(cyl);eval(expr(expr(`$`(mtcars,!!x))<=6))
返回:
[1]FALSE
这显然不是我期望的。如果我这样做:
eval(expr(`$`(mtcars,!!x)))<=6
返回
[1] TRUE TRUE TRUE TRUE FALSE TRUE FALSE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE FALSE
[23] FALSE FALSE FALSE TRUE TRUE TRUE FALSE TRUE FALSE TRUE
这是我要在函数内部使用filter()
函数的功能。所以我猜想当filter()
评估()
的内容时,它会自动将整个表达式放在方括号内,就像
eval(expr(expr(`$`(mtcars,!!x))<=6))
did,仅返回一个FALSE。因此,如果这真的是我期望的原因,那么如何让filter()
知道我真正想要的是要像这样进行评估:
eval(filter1<-expr(`$`(df,!!var2)))<=var2_upper
不是这个:
eval(filter1<-expr(`$`(df,!!var2))<=var2_upper)
如果我猜不是正在发生的事情,请也帮助我。
通常,我强烈建议您不要使用所有引用和评估方法。简洁的评估框架提供了更易于使用的替代工具。
以mtcars为例,如果我想通过说我只想要具有
cyl > 4
和mpg > 15
的行来对数据进行子集>典型的包装器功能如下:
filter2 <- function(data, var1, var2, lower1, lower2) {
data %>%
filter(
{{ var1 }} > .env$lower1,
{{ var2 }} > .env$lower2
)
}
使用{{
运算符,我们在数据上下文内插输入表达式。这意味着您可以提供直接引用列名称的R代码。
使用.env$
,我们要求在函数环境中使用lower
变量。这意味着,如果数据帧包含列lower1
和lower2
,则这些列不会干扰。在环境中强制评估的另一种方法是使用!!
。
mtcars %>% filter2(cyl, mpg, 4, 15) %>% head() #> mpg cyl disp hp drat wt qsec vs am gear carb #> 1 21 6 160 110 3.9 2.6 16 0 1 4 4 #> 2 21 6 160 110 3.9 2.9 17 0 1 4 4 #> 3 21 6 258 110 3.1 3.2 19 1 0 3 1 #> 4 19 8 360 175 3.1 3.4 17 0 0 3 2 #> 5 18 6 225 105 2.8 3.5 20 1 0 3 1 #> 6 19 6 168 123 3.9 3.4 18 1 0 4 4
================================>
此答案的其余部分试图解开您提出的一些难题。这可能有助于更好地了解R中的评估模型,但是同样,最好还是找到更简单的方法来解决问题。
让我们来:
x<-expr(cyl);eval(expr(expr(`$`(mtcars,!!x))<=6)) #> [1] FALSE
重新格式化:
x <- expr(cyl) eval(expr(expr(`$`(mtcars,!!x)) <= 6))
消除不必要的复杂性:
eval(expr(expr(mtcars$cyl) <= 6))
让我们看一下中间结果:
expr(expr(mtcars$cyl) <= 6) #> expr(mtcars$cyl) <= 6
外部
expr()
返回一个指示R的表达式:
- [创建一个新表达式(带有内部
expr()
)
6
进行比较[不幸的是,R表达式是可比较的,即使它没有任何意义。在理想情况下,这将是一个错误:
quote(foo) < 10 #> [1] FALSE
可能您想做的是先计算表达式中描述的列子设置,然后与
<=
比较:eval(expr(mtcars$cyl)) <= 6 #> [1] TRUE TRUE TRUE TRUE FALSE TRUE FALSE TRUE TRUE TRUE #> [11] TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE #> [21] TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE FALSE TRUE #> [31] FALSE TRUE
另一注。您写道:
eval(filter1<-expr(`$`(df,!!var2)))
重新格式化和简化:
eval(filter1 <- expr(mtcars$cyl)))
这是您对此进行评估时所发生的事情:
[
eval()
要求R返回其第一个参数,以便可以对其求值。
R看到eval()
的参数是<-
调用。然后,它开始对其进行评估。
RHS是描述如何对mtcars
进行子集化的一种混淆表达。该RHS分配给LHS filter1
。
<-
不可见地返回RHS。这就是eval()
作为参数。
[eval()
继续计算mtcars
子设置。
@@ Lionel Henry谢谢!我对您的示例确实有后续问题。
filter2 <- function(data, var1, var2, lower1, lower2) {
data %>%
filter(
{{ var1 }} > .env$lower1,
{{ var2 }} > .env$lower2
)
}
如果我想要下面的内容该怎么办?基本上,我想从filter()中删除里面的内容,并将它们预先存储在一些变量中。我知道以下功能不起作用。我想知道如何编辑它才能使它工作。
filter2 <- function(data, var1, var2, lower1, lower2) {
filter_a<-{{ var1 }} > .env$lower1
filter_b<-{{ var2 }} > .env$lower2
data %>%
filter(filter_a,filter_b)
}
我想要这个的原因是因为出于我的功能目的,filter()内部的内容将是动态的。例如,我需要这样的东西:
###if both lower and upper boundary for var1 are given by the user,do below:
filter_a<-{{ var1 }} > .env$lower1&{{ var1 }} < .env$upper1
###if only upper are given.do below:
filter_a<-{{ var1 }} < .env$upper1
###if only lower are given, do below:
filter_a<-{{ var1 }} > .env$lower1
这也是为什么我在原来冗长且“难以阅读”的问题中有这么多if语句的原因。
@@ Lionel Henry谢谢!我对您的示例确实有后续问题。