Kon*_*rad 10 r function lazy-evaluation dataframe dplyr
根据对条件dplyr求值的讨论,我想根据传入数据帧中是否存在引用列,有条件地在管道中执行一个步骤.
所产生的结果1)和2)应该是相同的.
# 1)
mtcars %>%
filter(am == 1) %>%
filter(cyl == 4)
# 2)
mtcars %>%
filter(am == 1) %>%
{
if("cyl" %in% names(.)) filter(cyl == 4) else .
}
Run Code Online (Sandbox Code Playgroud)
# 1)
mtcars %>%
filter(am == 1)
# 2)
mtcars %>%
filter(am == 1) %>%
{
if("absent_column" %in% names(.)) filter(absent_column == 4) else .
}
Run Code Online (Sandbox Code Playgroud)
对于可用列,传递的对象与初始数据帧不对应.原始代码返回错误消息:
错误
filter(cyl == 4):'cyl'找不到对象
我尝试了其他语法(没有运气):
>> mtcars %>%
... filter(am == 1) %>%
... {
... if("cyl" %in% names(.)) filter(.$cyl == 4) else .
... }
Show Traceback
Rerun with Debug
Error in UseMethod("filter_") :
no applicable method for 'filter_' applied to an object of class "logical"
Run Code Online (Sandbox Code Playgroud)
我想扩大这个问题,这个问题将考虑==到filter电话会议右侧的评估.例如,下面的语法试图过滤第一个可用值.mtcars%>%
filter({
if ("does_not_ex" %in% names(.))
does_not_ex
else
NULL
} == {
if ("does_not_ex" %in% names(.))
unique(.[['does_not_ex']])
else
NULL
})
Run Code Online (Sandbox Code Playgroud)
预计,调用将评估错误消息:
错误
filter_impl(.data, quo):结果的长度必须为32,而不是0
应用于现有列时:
mtcars %>%
filter({
if ("mpg" %in% names(.))
mpg
else
NULL
} == {
if ("mpg" %in% names(.))
unique(.[['mpg']])
else
NULL
})
Run Code Online (Sandbox Code Playgroud)
它使用警告消息:
mpg cyl disp hp drat wt qsec vs am gear carb
1 21 6 160 110 3.9 2.62 16.46 0 1 4 4
Run Code Online (Sandbox Code Playgroud)
警告消息:In
{:较长的对象长度不是较短对象长度的倍数
有没有一种简洁的方法来扩展现有的语法,以便在filter呼叫的右侧获得条件评估,理想情况是保持在dplyr工作流程中?
Eum*_*ies 14
由于此处的作用域的工作方式,您无法从if语句中访问数据框.幸运的是,你不需要.
尝试:
mtcars %>%
filter(am == 1) %>%
filter({if("cyl" %in% names(.)) cyl else NULL} == 4)
Run Code Online (Sandbox Code Playgroud)
在这里,您可以使用条件中的' .'对象,以便检查列是否存在,如果存在,则可以将列返回到filter函数.
编辑:根据docendo discimus'对问题的评论,你可以访问数据框但不是隐含的 - 即你必须专门引用它 .
Fel*_*ard 10
我知道我参加聚会迟到了,但这里的答案更符合您最初的想法:
mtcars %>%
filter(am == 1) %>%
{
if("cyl" %in% names(.)) filter(., cyl == 4) else .
}
Run Code Online (Sandbox Code Playgroud)
基本上,你错过了.in filter。请注意,这是因为管道不会添加.到,filter(expr)因为它位于由{}.
s_p*_*ike 10
随着across()在dplyr> 1.0.0现在可以使用any_of过滤时。将原始数据与所有列进行比较:
mtcars %>%
filter(am == 1) %>%
filter(cyl == 4)
Run Code Online (Sandbox Code Playgroud)
随着cyl去除,它抛出一个错误:
mtcars %>%
select(!cyl) %>%
filter(am == 1) %>%
filter(cyl == 4)
Run Code Online (Sandbox Code Playgroud)
使用any_of(注意你必须写"cyl"而不是cyl):
mtcars %>%
select(!cyl) %>%
filter(am == 1) %>%
filter(across(any_of("cyl"), ~.x == 4))
#N.B. this is equivalent to just filtering by `am == 1`.
Run Code Online (Sandbox Code Playgroud)
在忙碌的一天,人们可能会这样做:
library(dplyr)
df <- data.frame(A = 1:3, B = letters[1:3], stringsAsFactors = F)
> df %>% mutate( C = ifelse("D" %in% colnames(.), D, B))
# Notice the values on "C" colum. No error thrown, but the logic and result is wrong
A B C
1 1 a a
2 2 b a
3 3 c a
Run Code Online (Sandbox Code Playgroud)
为什么?因为"D" %in% colnames(.)只返回TRUEor 的一个值FALSE,因此ifelse只运行一次。然后将值广播到整列!
> df %>% mutate( C = if("D" %in% colnames(.)) D else B)
A B C
1 1 a a
2 2 b b
3 3 c c
Run Code Online (Sandbox Code Playgroud)