names(df)[!!colSums(is.na(df))]
#[1] "b" "c"
说明
colSums(is.na(df)) #gives you the number of missing value per each columns
#a b c
#0 1 1
通过使用!,我们正在创建一个逻辑索引
!colSums(is.na(df)) #here the value of `0` will be `TRUE` and all other values `>0` FALSE
# a b c
#TRUE FALSE FALSE
但是,我们需要选择那些至少有一个NA的列,所以!再次否定
!!colSums(is.na(df))
# a b c
#FALSE TRUE TRUE
并使用这个逻辑索引来获取至少有一个NA的colnames
基准测试
set.seed(49)
df1 <- as.data.frame(matrix(sample(c(NA,1:200), 1e4*5000, replace=TRUE), ncol=5000))
library(microbenchmark)
f1 <- function() {contains_any_na = sapply(df1, function(x) any(is.na(x)))
names(df1)[contains_any_na]}
f2 <- function() {colnames(df1)[!complete.cases(t(df1))] }
f3 <- function() { names(df1)[!!colSums(is.na(df1))] }
microbenchmark(f1(), f2(), f3(), unit="relative")
#Unit: relative
#expr min lq median uq max neval
#f1() 1.000000 1.000000 1.000000 1.000000 1.000000 100
#f2() 8.921109 7.289053 6.852122 6.210826 4.889684 100
#f3() 3.248072 3.105798 2.984453 2.774513 2.599745 100
EDIT性能说明:
也许令人惊讶的是,基于sapply 的解决方案是这里的赢家,因为正如下面@flodel 评论中所述,其他两个解决方案在幕后创建了一个矩阵(t(df) 和is.na(df))创建矩阵。