Как удалить столбцы, содержащие ТОЛЬКО НА?


83

У меня есть data.frame, содержащий несколько столбцов со всеми значениями NA, как я могу удалить их из data.frame.

Могу ли я использовать функцию

na.omit(...) 

указание дополнительных аргументов?


1
Всем привет! Пожалуйста, сделайте свой пост воспроизводимым. Прочтите сообщение о том, как сделать отличный воспроизводимый пример того, как это сделать. Спасибо.
Arun

Помогает ли этот пост? stackoverflow.com/questions/4862178/…
Arun

ты можешь опубликовать head(data)? Вы хотите удалить соответствующие столбцы или строки?
Nishanth

@ e4e5f4 Я хочу удалить соответствующие столбцы (все значения столбцов, которые я хочу удалить, являются NA)
Лоренцо Ригамонти

Ответы:


123

Один из способов сделать это:

df[, colSums(is.na(df)) != nrow(df)]

Если количество NA в столбце равно количеству строк, оно должно быть полностью NA.

Или аналогично

df[colSums(!is.na(df)) > 0]

1
Как я могу удалить столбцы, у которых пороговое значение превышает NA? или в процентах (скажем, выше 50%)?
ученик

2
@lovedynasty Вероятно, лучше всего задать отдельный вопрос, если вы еще не сделали этого с момента публикации своего комментария. Но в любом случае вы всегда можете сделать что-то вроде, df[, colSums(is.na(df)) < nrow(df) * 0.5]например, оставить столбцы, по крайней мере, с 50% непустыми.
Киаран Тобин,

2
Люди, работающие с корреляционной матрицей, должны использовать ее, df[, colSums(is.na(df)) != nrow(df) - 1]поскольку диагональ всегда 1
равна

9
Это также можно использовать с функцией select_if dplyr (версия 0.5.0). df %>% select_if(colSums(!is.na(.)) > 0)
Стефан Авей

@MadScone дает мне синтаксическую ошибку в "," для df [, colSums (is.na (df))! = Nrow (df)] и синтаксическую ошибку в "!" в df [colSums (! is.na (df))> 0]. Я что-то
упускаю

53

Вот решение dplyr:

df %>% select_if(~sum(!is.na(.)) > 0)

4
При ~ 15 тыс. Строк и ~ 5 тыс. Столбцов это действительно занимает вечность.
EngrStudent

@EngrStudent Было ли это быстрее с принятым решением ответа?
Джонни

Прошло много лет. Не помню. Ниже у DJV есть хороший пост о времени.
EngrStudent


24

Похоже, вы хотите удалить ТОЛЬКО столбцы со ВСЕМИ NA s, оставив столбцы с некоторыми строками, у которых есть NAs. Я бы сделал это (но уверен, что есть эффективный векторизованный сценарий:

#set seed for reproducibility
set.seed <- 103
df <- data.frame( id = 1:10 , nas = rep( NA , 10 ) , vals = sample( c( 1:3 , NA ) , 10 , repl = TRUE ) )
df
#      id nas vals
#   1   1  NA   NA
#   2   2  NA    2
#   3   3  NA    1
#   4   4  NA    2
#   5   5  NA    2
#   6   6  NA    3
#   7   7  NA    2
#   8   8  NA    3
#   9   9  NA    3
#   10 10  NA    2

#Use this command to remove columns that are entirely NA values, it will elave columns where only some vlaues are NA
df[ , ! apply( df , 2 , function(x) all(is.na(x)) ) ]
#      id vals
#   1   1   NA
#   2   2    2
#   3   3    1
#   4   4    2
#   5   5    2
#   6   6    3
#   7   7    2
#   8   8    3
#   9   9    3
#   10 10    2

Если вы оказались в ситуации, когда вы хотите удалить столбцы с любыми NAзначениями, вы можете просто изменить приведенную allвыше команду на any.


Data.frame имеет два типа столбцов: один, в котором все значения являются числами, а другой, в котором все значения являются NA,
Лоренцо Ригамонти,

Тогда это сработает. Он удаляет только столбцы, в которых есть ВСЕ значения NA.
Саймон О'Хэнлон

1
Хорошее решение. Я бы сделал это apply(is.na(df), 1, all)просто потому, что он немного аккуратнее и is.na()используется для всех, dfа не для одной строки за раз (покажите, будет немного быстрее).
Ciarán Tobin

@MadScone хороший совет - выглядит аккуратнее. Однако вы должны применять к столбцам, а не к строкам.
Саймон О'Хэнлон

@MadScone Изменения блокируются через 5 минут после комментариев. Я не должен волноваться, это не страшно !! :-)
Саймон О'Хэнлон

20

Интуитивный сценарий: dplyr::select_if(~!all(is.na(.))). Он буквально сохраняет только столбцы, в которых отсутствуют все элементы. (чтобы удалить столбцы с отсутствующими элементами).

> df <- data.frame( id = 1:10 , nas = rep( NA , 10 ) , vals = sample( c( 1:3 , NA ) , 10 , repl = TRUE ) )

> df %>% glimpse()
Observations: 10
Variables: 3
$ id   <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
$ nas  <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA
$ vals <int> NA, 1, 1, NA, 1, 1, 1, 2, 3, NA

> df %>% select_if(~!all(is.na(.))) 
   id vals
1   1   NA
2   2    1
3   3    1
4   4   NA
5   5    1
6   6    1
7   7    1
8   8    2
9   9    3
10 10   NA

17

Другой вариант с Filter

Filter(function(x) !all(is.na(x)), df)

ПРИМЕЧАНИЕ: данные из сообщения @Simon O'Hanlon.


5

Поскольку для меня действительно была важна производительность, я протестировал все вышеперечисленные функции.

ПРИМЕЧАНИЕ: данные из сообщения @Simon O'Hanlon. Только размером 15000 вместо 10.

library(tidyverse)
library(microbenchmark)

set.seed(123)
df <- data.frame(id = 1:15000,
                 nas = rep(NA, 15000), 
                 vals = sample(c(1:3, NA), 15000,
                               repl = TRUE))
df

MadSconeF1 <- function(x) x[, colSums(is.na(x)) != nrow(x)]

MadSconeF2 <- function(x) x[colSums(!is.na(x)) > 0]

BradCannell <- function(x) x %>% select_if(~sum(!is.na(.)) > 0)

SimonOHanlon <- function(x) x[ , !apply(x, 2 ,function(y) all(is.na(y)))]

jsta <- function(x) janitor::remove_empty(x)

SiboJiang <- function(x) x %>% dplyr::select_if(~!all(is.na(.)))

akrun <- function(x) Filter(function(y) !all(is.na(y)), x)

mbm <- microbenchmark(
  "MadSconeF1" = {MadSconeF1(df)},
  "MadSconeF2" = {MadSconeF2(df)},
  "BradCannell" = {BradCannell(df)},
  "SimonOHanlon" = {SimonOHanlon(df)},
  "SiboJiang" = {SiboJiang(df)},
  "jsta" = {jsta(df)}, 
  "akrun" = {akrun(df)},
  times = 1000)

mbm

Полученные результаты:

Unit: microseconds
         expr    min      lq      mean  median      uq      max neval  cld
   MadSconeF1  154.5  178.35  257.9396  196.05  219.25   5001.0  1000 a   
   MadSconeF2  180.4  209.75  281.2541  226.40  251.05   6322.1  1000 a   
  BradCannell 2579.4 2884.90 3330.3700 3059.45 3379.30  33667.3  1000    d
 SimonOHanlon  511.0  565.00  943.3089  586.45  623.65 210338.4  1000  b  
    SiboJiang 2558.1 2853.05 3377.6702 3010.30 3310.00  89718.0  1000    d
         jsta 1544.8 1652.45 2031.5065 1706.05 1872.65  11594.9  1000   c 
        akrun   93.8  111.60  139.9482  121.90  135.45   3851.2  1000 a


autoplot(mbm)

введите описание изображения здесь

mbm %>% 
  tbl_df() %>%
  ggplot(aes(sample = time)) + 
  stat_qq() + 
  stat_qq_line() +
  facet_wrap(~expr, scales = "free")

введите описание изображения здесь


Иногда первая итерация - это JIT-компиляция, поэтому у нее очень плохое и не очень характерное время. Я думаю, что интересно, что делает больший размер выборки с правильными хвостами распределения. Это хорошая работа.
EngrStudent

Я запустил его еще раз, не был уверен, что изменил сюжет. Что касается раздачи. Мне, вероятно, следует сравнить разные размеры выборок, когда у меня будет время.
DJV

1
Если у вас qqplot ( ggplot2.tidyverse.org/reference/geom_qq.html ) одна из тенденций, такая как "akrun", то я уверен, что есть одна точка, которая сильно отличается от распределения остальных. Остальные показывают, сколько времени потребуется, если вы запустите его несколько раз, но это означает, что произойдет, если вы запустите его один раз. Есть старая пословица: у вас может быть 20-летний опыт работы или 20-кратный опыт работы только за один год.
EngrStudent

очень хорошо! Я удивлен тем, что несколько образцов оказались в крайнем хвосте. Интересно, почему они намного дороже? JIT может быть 1 или 2, но не 20. Условие? Прерывает? Разное? Еще раз спасибо за обновление.
EngrStudent

Пожалуйста, спасибо за мысли. Не знаю, я на самом деле позволил ему работать «свободно».
DJV
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.