如果 R 中所有值均为 NA,则删除特定列

Bob*_*son 4 r tidyverse

我有一个从 R 运行的数据库查询,它可以有多个填充 NA 的列,其中一些列我需要进一步向下,但如果所有值都是 NA,则可以删除一个特定列。

我通常使用 purrr::discard(~all(is.na(.))) 删除全部 NA 的列,但由于此数据帧可以包含多个带有 NA 的列,我只想删除一个我正在努力删除的列使其特定于 tidyverse 解决方案中的列。

我目前有这个解决方法:

  if(sum(is.na(Orders$Originator)) == nrow(Orders)) {
    
    Orders <- Orders %>%
      select(-Originator)
    
  }
Run Code Online (Sandbox Code Playgroud)

但如果我可以在 tidyverse 解决方案中使用它,它会提高可读性。希望有人可以提供帮助!

谢谢!

Tim*_*Fan 6

解决此问题的规范 tidyverse 方法是利用内部使用的谓词函数select(where(...)),并将其与按变量名称进行选择相结合。

首先,我们可以编写一个自定义谓词函数,在其中where仅选择仅包含NAs 的列。

# custom predicate function
all_na <- function(x) {
  all(is.na(x))
}
Run Code Online (Sandbox Code Playgroud)

我们可以将此函数与布尔表达式一起使用,表示我们不想ifselect y (read AND &) it is all_na

library(dplyr)

df <- data.frame(
  x = c(1,2,NA),
  y = NA,
  z = c(3,4,5)
)

df %>% 
  select(!(y & where(all_na)))
#>    x z
#> 1  1 3
#> 2  2 4
#> 3 NA 5
Run Code Online (Sandbox Code Playgroud)

为了检查这是否真的有效,让我们重新定义y,使其不仅包含NAs,我们将看到这次它没有被取消选择:

df2 <- data.frame(
  x = c(1,2,NA),
  y = c(1,2,NA),
  z = c(3,4,5)
)

df2 %>% 
  select(!(y & where(all_na)))
#>    x  y z
#> 1  1  1 3
#> 2  2  2 4
#> 3 NA NA 5
Run Code Online (Sandbox Code Playgroud)

我们可以在内部使用 lambda 函数来代替自定义函数where

df %>% 
  select(!(y & where(~ all(is.na(.x)))))
Run Code Online (Sandbox Code Playgroud)

由reprex 包于 2021 年 12 月 7 日创建(v0.3.0)


在更大的 tidyverse 中,我们还可以使用参数purrr::lmap_at并选择,然后创建一个 lambda 函数,说明 if then 使用空(= 删除列),否则保留列:y.atall(is.na(.x))list().x

library(purrr)
library(dplyr)

df %>% 
  lmap_at("y", ~ if(all(is.na(.x))) list() else .x)
#> # A tibble: 3 x 2
#>       x     z
#>   <dbl> <dbl>
#> 1     1     3
#> 2     2     4
#> 3    NA     5
Run Code Online (Sandbox Code Playgroud)

由reprex 包于 2021 年 12 月 7 日创建(v2.0.1)