Просмотр всех имен столбцов с любым NA в R


Мне нужно получить имена столбцов, которые имеют по крайней мере 1 NA.

df<-data.frame(a=1:3,b=c(NA,8,6), c=c('t',NA,7))

Мне нужно получить "b, c".

Я нашел этот код:

sapply(df, function(x) any(is.na(x)))

Но мне нужны только переменные, у которых есть какие-либо NA.

Я попробовал это:

sapply(df, function(x) colnames(df[,any(is.na(x))]))

Но я получаю все имена столбцов.

5 6

5 ответов:

Еще одно акробатическое решение (просто для удовольствия):

colnames(df)[!complete.cases(t(df))]
[1] "b" "c"

Идея такова: получение столбцов A, которые имеют по крайней мере 1 NA, эквивалентно получению строк, которые имеют по крайней мере NA для t(A). complete.cases по определению (очень эффективно, так как это просто вызов функции C) дает строки без какого-либо пропущенного значения.

Вы были очень близки. Ваша первая попытка дает вектор boolean, который вы можете использовать для индексации names из df:

contains_any_na = sapply(df, function(x) any(is.na(x)))
names(df)[contains_any_na]
# [1] "b" "c"

Обновление 14 января 2017 года: начиная с версии 3.1.0 R, anyNA() можно использовать в качестве альтернативы any(is.na(.)), а приведенный выше код можно упростить до

names(df)[sapply(df, anyNA)]
# [1] "b" "c"
 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 

И используйте этот логический индекс, чтобы получить имена colnames, которые имеют по крайней мере один NA

Контрольные показатели

 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

Изменить объяснение производительности:

Возможно, удивительное решение на основе sapply является здесь победителем, потому что, как отмечено в комментарии @flodel ниже, 2 другие решения создали матрицу за сценой (t(df) и is.na(df)) создать матрицу.

Попробуйте данные.версия таблицы:

library(data.table)
setDT(df)
names(df)[df[,sapply(.SD, function(x) any(is.na(x))),]]
[1] "b" "c"

Микробенчмаркинг с использованием кода @akrun:

set.seed(49)
df1 <- as.data.frame(matrix(sample(c(NA,1:200), 1e4*5000, replace=TRUE), ncol=5000))
setDT(df1)


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))] }

f4 <- function() { names(df1)[df1[,sapply(.SD, function(x) any(is.na(x))),]] }

microbenchmark(f1(), f2(), f3(), f4(), unit="relative")   
# Unit: relative
#  expr       min        lq    median       uq      max neval
#  f1()  1.000000  1.000000  1.000000 1.000000 1.000000   100
#  f2() 10.459124 10.928821 10.955986 9.858967 7.069066   100
#  f3()  3.323144  3.805183  4.159624 3.775549 2.797329   100
#  f4() 10.108998 10.242207 10.121022 9.117067 6.576976   100

@agstudy: это решение аналогично по скорости colnames(df1)[!complete.cases(t(df1))].

Простой один лайнер для этого:

colnames(df[,sapply(df, function(x) any(is.na(x)))])

Пояснение:

sapply(df, function(x) any(is.na(x)))

Возвращает True / False для столбцов с по крайней мере 1 NA. df[,sapply(df, function(x) any(is.na(x)))] получает подмножество фрейма данных, в котором все его столбцы имеют по крайней мере 1 NA. И colnames дает названия этим столбцам.