Як видалити стовпці, що містять ТІЛЬКИ NA?


83

У мене є data.frame, що містить кілька стовпців із усіма значеннями NA, як я можу їх видалити з data.frame.

Чи можу я скористатися функцією

na.omit(...) 

вказуючи деякі додаткові аргументи?


1
Привіт! Будь ласка, зробіть вашу публікацію відтворюваною. Прочитайте пост, як зробити чудовий відтворюваний приклад того, як це зробити. Дякую.
Арун,

Чи допомагає цей пост? stackoverflow.com/questions/4862178/…
Арун

Ви можете розмістити head(data)? Видалити відповідні стовпці або рядки?
Нішант

@ e4e5f4 Я хочу видалити відповідні стовпці (усі значення стовпців, які я хочу видалити, є НС)
Лоренцо Рігамонті

Відповіді:


123

Один із способів зробити це:

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

Якщо кількість НС у стовпці дорівнює кількості рядків, це має бути повністю НС.

Або так само

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

1
Як я можу видалити стовпці, що мають більше ніж поріг NA? або у відсотках (скажімо, вище 50%)?
discipulus

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

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

9
Можна також використовувати це з функцією dplyr (версія 0.5.0) select_if. df %>% select_if(colSums(!is.na(.)) > 0)
Stefan Avey

@MadScone він видає мені синтаксичну помилку на "," для df [, colSums (is.na (df))! = Nrow (df)] та синтаксичну помилку на "!" у df [colSums (! is.na (df))> 0]. Мені чогось не вистачає
Aravind S

52

Ось рішення 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.
Саймон О'Хенлон

1
Хороше рішення. Я хотів би зробити це apply(is.na(df), 1, all)лише тому, що він трохи акуратніший і is.na()використовується в усіх, dfа не в одному рядку за раз (шоу буде трохи швидшим).
MadScone

Хороша підказка @MadScone - виглядає акуратніше. Вам слід застосовувати стовпці, а не рядки.
Саймон О'Хенлон

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

19

Інтуїтивний сценарій: 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.