NA 만 포함하는 열을 삭제하는 방법은 무엇입니까?


83

모든 NA 값이있는 일부 열을 포함하는 data.frame이 있습니다. data.frame에서 어떻게 삭제할 수 있습니까?

기능을 사용할 수 있습니까

na.omit(...) 

추가 인수를 지정 하시겠습니까?


1
안녕! 게시물을 재현 가능하게 만드십시오. 이를 수행하는 방법에 대한 재현 가능한 예제를 만드는 방법에 대한 게시물을 읽으십시오 . 감사합니다.
Arun

이 게시물이 도움이됩니까? stackoverflow.com/questions/4862178/…
Arun 2013

게시 할 수 head(data)있습니까? 해당 열이나 행을 제거 하시겠습니까?
Nishanth 2013

@ e4e5f4 해당 열을 제거하고 싶습니다 (제거하려는 열의 모든 값은 해당 없음)
Lorenzo Rigamonti 2013

답변:


123

한 가지 방법 :

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

열의 NA 수가 행 수와 같으면 완전히 NA 여야합니다.

또는 유사하게

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

1
NA 임계 값보다 많은 열을 삭제하려면 어떻게해야합니까? 또는 백분율 (50 % 이상이라고 말함)?
discipulus 2015 년

2
@lovedynasty 코멘트를 게시 한 이후로 아직하지 않았다고 가정하고 별도의 질문을 제출하는 것이 가장 좋습니다. 그러나 어쨌든, 당신은 항상 같은 일을 할 수 있습니다. df[, colSums(is.na(df)) < nrow(df) * 0.5]즉, 적어도 50 %는 공백이 아닌 열만 유지합니다.
MadScone 2011

2
상관 행렬을 사용하는 사람들 df[, colSums(is.na(df)) != nrow(df) - 1]은 대각선이 항상이기 때문에 사용해야합니다1
Boern

9
dplyr (버전 0.5.0) select_if 함수와 함께 사용할 수도 있습니다. df %>% select_if(colSums(!is.na(.)) > 0)
Stefan Avey 2016

@MadScone df [, colSums (is.na (df))! = nrow (df)]에 대해 ","에 구문 오류가 발생하고 "!"에 구문 오류가 있습니다. df [colSums (! is.na (df))> 0]. 내가 뭔가를 놓치고
있습니까

52

다음은 dplyr 솔루션입니다.

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

4
~ 15k 행과 ~ 5k 열에서 이것은 진정으로 영원합니다.
EngrStudent

@EngrStudent 수락 된 답변의 솔루션으로 더 빨랐습니까?
johnny

몇 년이 지났습니다. 기억이 안나요. DJV는 아래에 좋은 타이밍 포스트를 가지고 있습니다.
EngrStudent


24

제거 할 것처럼 seeems 에 열 ALL NA 해야 할 일부 행과 열을 떠나들 NA들. 나는 이것을 할 것입니다 (그러나 효율적인 벡터화 된 soution이 있다고 확신합니다.

#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입니다
Lorenzo Rigamonti 2013

그러면 이것은 작동합니다. 모든 값이 인 열만 제거합니다 NA.
Simon O'Hanlon 2013-04-12

1
좋은 솔루션입니다. 나는 apply(is.na(df), 1, all)그것이 약간 깔끔하고 한 번에 한 행 is.na()df아닌 모두에 사용 되기 때문에 할 것입니다 (조금 더 빨리 보여줍니다).
MadScone 2013

@MadScone 좋은 팁-깔끔해 보입니다. 하지만 행이 아닌 열에 적용해야합니다.
Simon O'Hanlon 2013

@MadScone 편집 내용은 댓글에 5 분 후에 잠 깁니다. 걱정하지 마세요, 별거 아니에요 !! :-)
Simon O'Hanlon 2013

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의 게시물 데이터. 10 대신 15000 크기 만 사용합니다.

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 년의 경험을 가질 수 있거나 1 년의 경험을 20 번만 가질 수 있습니다.
EngrStudent

아주 좋아요! 나는 극단적 인 꼬리에있는 몇몇 샘플에 놀랐다. 그게 왜 그렇게 더 비싸다는 건지 궁금합니다. JIT는 1 또는 2 일 수 있지만 20은 아닙니다. 조건? 인터럽트? 다른? 업데이트 해주셔서 다시 한 번 감사드립니다.
EngrStudent

천만에요, 생각에 감사드립니다. 모르겠습니다. 실제로 "자유롭게"실행되도록 허용했습니다.
DJV
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.