使用filter()和cross()保留数据帧中包含任何变量缺失值的所有行

使用filter()和cross()保留数据帧中包含任何变量缺失值的所有行,r,dplyr,r4epi,R,Dplyr,R4epi,有时,我想查看数据帧中的所有行,如果我删除了任何变量缺少值的所有行,这些行将被删除。在本例中,我特别感兴趣的是如何使用dplyr1.0的cross()函数来实现这一点 以下是一个示例数据帧: df <- tribble( ~id, ~x, ~y, 1, 1, 0, 2, 1, 1, 3, NA, 1, 4, 0, 0, 5, 1, NA ) 返回: # A tibble: 3 x 3 id x y <dbl> <db

有时,我想查看数据帧中的所有行,如果我删除了任何变量缺少值的所有行,这些行将被删除。在本例中,我特别感兴趣的是如何使用
dplyr
1.0的
cross()
函数来实现这一点

以下是一个示例数据帧:

df <- tribble(
  ~id, ~x, ~y,
  1, 1, 0,
  2, 1, 1,
  3, NA, 1,
  4, 0, 0,
  5, 1, NA
)
返回:

# A tibble: 3 x 3
     id     x     y
  <dbl> <dbl> <dbl>
1     1     1     0
2     2     1     1
3     4     0     0
但是,它返回零行

当然,如果我提前知道所有缺少值的变量,我可以通过这段代码得到我想要的答案:

df %>% 
  filter(is.na(x) | is.na(y))
但是,我正在寻找一种解决方案,它不需要我提前知道哪些变量缺少值。此外,我知道如何使用
filter\u all()
函数执行此操作:

df %>% 
  filter_all(any_vars(is.na(.)))
但是,
filter\u all()
函数已被现有动词中的
cross()
所取代。看

我所做的其他失败尝试有:

df %>% 
  filter(
    across(
      .cols = everything(),
      .fns = ~any_vars(is.na(.x))
    )
  )

df %>% 
  filter(
    across(
      .cols = everything(),
      .fns = ~!!any_vars(is.na(.x))
    )
  )

df %>% 
  filter(
    across(
      .cols = everything(),
      .fns = ~!!any_vars(is.na(.))
    )
  )

df %>% 
  filter(
    across(
      .cols = everything(),
      .fns = ~any(is.na(.x))
    )
  )

df %>% 
  filter(
    across(
      .cols = everything(),
      .fns = ~any(is.na(.))
    )
  )

我们可以使用
reduce

library(dplyr)
library(purrr)
df %>% 
      filter(across(everything(), is.na) %>% reduce(`|`))
# A tibble: 2 x 3
#     id     x     y
#  <dbl> <dbl> <dbl>
#1     3    NA     1
#2     5     1    NA
库(dplyr)
图书馆(purrr)
df%>%
过滤器(跨越(everything(),is.na)%>%reduce(`|`))
#一个tibble:2x3
#id x y
#    
#1 3 NA 1
#2 5 1 NA

以下是我对此的看法。我对新的
cross()
函数的理解是它对列而不是行进行操作。所以当你运行这段代码时,你不会得到任何东西,因为

df%>%
滤器(
穿过(
.cols=一切(),
.fns=~is.na(.x)
)
)
#一个tibble:0 x 3
#…具有3个变量:id、x、y
它基本上在后台创建三个逻辑列来检查元素方面的
NA
的存在,然后可能会计算最后一个逻辑列,在该逻辑列上运行filter命令。现在,只有当
is.na()
的所有列都是
TRUE
时,最后一列才具有
TRUE
。为了验证我的假设,我在您的数据中添加了另一行,这三列中都有
NA
。当我按原样运行代码时,我得到该行作为输出,因为现在相应的行将在最终的逻辑向量中生成一个
TRUE
。现在我不知道
cross()
是否就是这样工作的,但这对我来说是有意义的

库(tidyverse)
df%
滤器(
穿过(
.cols=一切(),
.fns=~is.na(.x)
)
)
#>#A tible:1 x 3
#>id x y
#>     
#>1NA NA NA
因此,为了使其与原始数据一起工作,我将使用
rowwise()
c_-cross()
(rowwise版本的
cross()
)如下

df%>%rowwise()%>%
滤器(
is.na(sum(c_横跨(一切()))
) %>% 
解组()
#>#tibble:3 x 3
#>id x y
#>     
#>1 3 NA 1
#>2 5 1 NA
df%%>%rowwise()%%>%
滤器(
any(is.na(c_横跨(everything()))
) %>% 
解组()
#>#tibble:3 x 3
#>id x y
#>     
#>1 3 NA 1
#>2 5 1 NA

HanOostdijk于2020-06-02由(v0.3.0)

创建,并在上回复了使用
cross()
函数的解决方案。他写道:

“在同一篇文章中,您提到rowSums函数有一个技巧。您可以将其用作:”


我认为使用它可能会有所帮助

setdiff(df,df%>%drop\u na())

为了得到你想要的结果

这不使用Cross(),而是以简单的方式完成作业。

df %>% anti_join(df %>% drop_na())

现在可以使用
dplyr
1.0.4。对于过滤用例,新的
if_any()
将替换
cross()

库(dplyr)
df%
过滤器(如果有(everything(),is.na))
#>#tibble:2 x 3
#>id x y
#>     
#>1 3 NA 1
#>2 5 1 NA
由(v0.3.0)于2021年2月10日创建


有关更多详细信息,请参见此处:

我发现了一种仅使用base R的方法:

df[apply(is.na.data.frame(df), 1, any), ]

我希望这对你有帮助。

谢谢你,@akrun!我真的很感谢你的回答,你的回答肯定有效。然而,它不觉得有点不令人满意吗?“这难道不应该更直截了当吗?”布拉德坎奈尔说。你是对的。我还感觉到,当我们跨使用
时,用
任何变量/所有变量
替换
处的
过滤器并不清楚。我希望下一版本的
dplyr
能够解决这些问题。我想我已经明白了为什么Cross()让我感到有点不舒服。我认为这是因为在我看来,
cross()
应该只选择要操作的列(在每个函数的精神中,每个函数只做一件事)。实际上,
cross()
用于选择要操作的列并接收要执行的操作。对我来说,我认为
cross()
如果能像这样使用会感觉更自然,例如:
df%>%groupby(g1,g2)%%>%summary(cross(a:d),mean)
而不是:
df%>%groupby(g1,g2)%%>%summary(cross(a:d,mean))
。“我相信这是有充分理由的。”布拉德坎内尔,谢谢,我也这么认为。@cropgen说,最好在他们的github页面上建议修改行为,这样他们会注意到这一点,并在发现更自然的情况下做出改变。我很感谢您能够提交一个仅限dplyr的解决方案。但是不仅仅是觉得应该有一个解决方案可以直接在
cross()
中工作吗?是的,我真的希望
cross()
可以工作,但遗憾的是,它没有或者我不知道如何使用它。不,
c\u cross()
在这里是合适的。如果要将列视为向量,则使用
cross()
;如果要将行视为向量,则使用
c\u cross()
。在本例中,我想对每一行向量应用
is.na%>%any
,这就是为什么我使用
c_overs()
。像这样的水平操作正是
c_a
library(dplyr)
library(purrr)
df %>% 
      filter(across(everything(), is.na) %>% reduce(`|`))
# A tibble: 2 x 3
#     id     x     y
#  <dbl> <dbl> <dbl>
#1     3    NA     1
#2     5     1    NA
rowAny <- function(x) {
  rowSums(x) > 0
} 

df %>% 
  filter(
    rowAny(
      across(
       .cols = everything(),
       .fns = ~ is.na(.x)
      )
    )
  )
df %>% 
  filter(rowSums(across(everything(), ~ is.na(.))) > 0)
df %>% anti_join(df %>% drop_na())
df[apply(is.na.data.frame(df), 1, any), ]