在R中,如果所有值都是NA,则删除特定列。

4

我正在使用 R 进行数据库查询,结果中可能会有多列为 NA。尽管后面需要使用其中一些列,但如果特定的列所有值都为 NA,那么可以将该列删除。

通常情况下,我会使用 purrr::discard(~all(is.na(.))) 去除所有值为 NA 的列,但由于数据框可能包含多个需要删除的 NA 列,而我只想删除其中的一列,在 tidyverse 解决方案中比较困难。

目前的解决方法如下:

  if(sum(is.na(Orders$Originator)) == nrow(Orders)) {
    
    Orders <- Orders %>%
      select(-Originator)
    
  }

如果我能有一个整洁的解决方案,这将提高可读性。希望有人能够提供帮助!

谢谢!

3个回答

6

解决这个问题的规范tidyverse方式是利用一个谓词函数,该函数在select(where(...))中使用,并将其与变量名选择相结合。

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

# custom predicate function
all_na <- function(x) {
  all(is.na(x))
}

我们可以使用这个函数和一个布尔表达式配合使用,该布尔表达式说明当(read AND &)它为all_na时,我们不想选择y
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

为了确定这是否有效,让我们重新定义y,使其不仅包含NA,我们将看到这一次它没有被取消选择:
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

我们可以在where语句中使用lambda函数来替代自定义函数:

df %>% 
  select(!(y & where(~ all(is.na(.x)))))

此内容由reprex包(v0.3.0)于2021-12-07创建


在更大的tidyverse中,我们还可以使用purrr::lmap_at并使用.at参数选择y,然后创建一个lambda函数,如果all(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

本段内容由 reprex 包 (v2.0.1) 于 2021-12-07 创建


1

使用示例数据:

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

这里的列y是目标列,用于检查是否all is.na。你的ifelse将包含在花括号中。这些花括号将阻止管道使用函数中的第一个参数。请注意,如果条件为假,则else将使数据框保留在管道中。

library(tidyverse)

df %>%
  { if (all(is.na(.$y))) select(., -y) else . }

输出

   x z
1  1 3
2  2 4
3 NA 5

不知道我们可以像这样集成函数。运行得非常好!非常感谢! - Bob Janson

1

看起来你正在尝试通过名称(即仅特定列)和逻辑混合选择。在tidyverse中,可以分别很好地完成这两种操作(使用整洁的选择器或where),但我不确定如何将它们组合起来!

因此,这里有一个不使用它们的简单解决方案:

library(dplyr, warn.conflicts = FALSE)
df <- data.frame(
  x = c(1,2,NA),
  y = NA,
  y2 = NA,
  z = c(3,4,5)
)

df %>% 
  select(-which(colnames(.)=="y" & sapply(., \(x) all(is.na(x)))))
#>    x y2 z
#> 1  1 NA 3
#> 2  2 NA 4
#> 3 NA NA 5

2021年12月07日,使用reprex软件包(v2.0.1)创建


这个解决方案的问题在于,如果您添加了一列,例如一个带有所有NA值的a,那么它也会被删除。我有更多的列可能包含完整的NA,但我只想删除其中一个。 - Bob Janson

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