Просмотр всех имен столбцов с любым 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 ответов:
Еще одно акробатическое решение (просто для удовольствия):
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
дает названия этим столбцам.