필드 - data.frame의 모든 또는 일부 NAs(누락 값)가있는 행 제거




파이썬 데이터프레임 행 삭제 (10)

성능이 우선 순위 인 경우 data.tablena.omit() 을 선택적 param cols= 와 함께 사용 data.table .

na.omit.data.table 은 모든 열 또는 선택한 열 (OP 질문 파트 2)에 상관없이 내 벤치 마크에서 가장 빠릅니다 (아래 참조).

data.table 을 사용하지 않으려면 complete.cases() .

바닐라 na.omit() 에서 complete.casesna.omit() 또는 dplyr::drop_na() 보다 na.omit() . na.omit.data.framecols= 지원하지 않습니다.

벤치 마크 결과

독립적 인 5 % 확률로 20 개의 수치 변수를 1 백만 관측 한 관념 자료 집합에 대해 모두 삭제하거나 누락 된 관측치를 삭제하는 기본 (파란색), dplyr (분홍색) 및 data.table (노란색) 방법을 비교합니다 누락 및 파트 2에 대한 4 개 변수의 서브 세트.

결과는 특정 데이터 세트의 길이, 너비 및 희소성에 따라 다를 수 있습니다.

y 축의 로그 스케일을 기록하십시오.

벤치 마크 스크립트

#-------  Adjust these assumptions for your own use case  ------------
row_size   <- 1e6L 
col_size   <- 20    # not including ID column
p_missing  <- 0.05   # likelihood of missing observation (except ID col)
col_subset <- 18:21  # second part of question: filter on select columns

#-------  System info for benchmark  ----------------------------------
R.version # R version 3.4.3 (2017-11-30), platform = x86_64-w64-mingw32
library(data.table); packageVersion('data.table') # 1.10.4.3
library(dplyr);      packageVersion('dplyr')      # 0.7.4
library(tidyr);      packageVersion('tidyr')      # 0.8.0
library(microbenchmark)

#-------  Example dataset using above assumptions  --------------------
fakeData <- function(m, n, p){
  set.seed(123)
  m <-  matrix(runif(m*n), nrow=m, ncol=n)
  m[m<p] <- NA
  return(m)
}
df <- cbind( data.frame(id = paste0('ID',seq(row_size)), 
                        stringsAsFactors = FALSE),
             data.frame(fakeData(row_size, col_size, p_missing) )
             )
dt <- data.table(df)

par(las=3, mfcol=c(1,2), mar=c(22,4,1,1)+0.1)
boxplot(
  microbenchmark(
    df[complete.cases(df), ],
    na.omit(df),
    df %>% drop_na,
    dt[complete.cases(dt), ],
    na.omit(dt)
  ), xlab='', 
  main = 'Performance: Drop any NA observation',
  col=c(rep('lightblue',2),'salmon',rep('beige',2))
)
boxplot(
  microbenchmark(
    df[complete.cases(df[,col_subset]), ],
    #na.omit(df), # col subset not supported in na.omit.data.frame
    df %>% drop_na(col_subset),
    dt[complete.cases(dt[,col_subset,with=FALSE]), ],
    na.omit(dt, cols=col_subset) # see ?na.omit.data.table
  ), xlab='', 
  main = 'Performance: Drop NA obs. in select cols',
  col=c('lightblue','salmon',rep('beige',2))
)

이 데이터 프레임의 행을 다음과 같이 제거하고 싶습니다.

a) 모든 열에 걸쳐 NA 포함해야합니다. 아래는 나의 예제 데이터 프레임이다.

             gene hsap mmul mmus rnor cfam
1 ENSG00000208234    0   NA   NA   NA   NA
2 ENSG00000199674    0   2    2    2    2
3 ENSG00000221622    0   NA   NA   NA   NA
4 ENSG00000207604    0   NA   NA   1    2
5 ENSG00000207431    0   NA   NA   NA   NA
6 ENSG00000221312    0   1    2    3    2

기본적으로 다음과 같은 데이터 프레임을 얻고 싶습니다.

             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0   2    2    2    2
6 ENSG00000221312    0   1    2    3    2

b) 일부 열에 만 NA 포함 NA 결과를 얻을 수도 있습니다.

             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0   2    2    2    2
4 ENSG00000207604    0   NA   NA   1    2
6 ENSG00000221312    0   1    2    3    2

dplyr 패키지를 사용하여 다음과 같이 NA를 필터링 할 수 있습니다.

dplyr::filter(df,  !is.na(columnname))

귀하의 첫 번째 질문에, 나는 모든 NA를 없애기에 편한 코드를 가지고 있습니다. @Gregor가 더 간단하게 해주셔서 감사합니다.

final[!(rowSums(is.na(final))),]

두 번째 질문의 경우, 코드는 이전 솔루션의 대안 일뿐입니다.

final[as.logical((rowSums(is.na(final))-5)),]

-5는 데이터의 열 수입니다. rowSums가 최대 5를 더하고 뺄셈 후에는 0이되기 때문에 모든 NA로 행을 제거 할 것입니다. 이번에는 as.logical이 필요합니다.


나는 신디사이저이다 :). 여기에 답을 하나의 함수로 결합했습니다.

#' keep rows that have a certain number (range) of NAs anywhere/somewhere and delete others
#' @param df a data frame
#' @param col restrict to the columns where you would like to search for NA; eg, 3, c(3), 2:5, "place", c("place","age")
#' \cr default is NULL, search for all columns
#' @param n integer or vector, 0, c(3,5), number/range of NAs allowed.
#' \cr If a number, the exact number of NAs kept
#' \cr Range includes both ends 3<=n<=5
#' \cr Range could be -Inf, Inf
#' @return returns a new df with rows that have NA(s) removed
#' @export
ez.na.keep = function(df, col=NULL, n=0){
    if (!is.null(col)) {
        # R converts a single row/col to a vector if the parameter col has only one col
        # see https://radfordneal.wordpress.com/2008/08/20/design-flaws-in-r-2-%E2%80%94-dropped-dimensions/#comments
        df.temp = df[,col,drop=FALSE]
    } else {
        df.temp = df
    }

    if (length(n)==1){
        if (n==0) {
            # simply call complete.cases which might be faster
            result = df[complete.cases(df.temp),]
        } else {
            # credit: http://.com/a/30461945/2292993
            log <- apply(df.temp, 2, is.na)
            logindex <- apply(log, 1, function(x) sum(x) == n)
            result = df[logindex, ]
        }
    }

    if (length(n)==2){
        min = n[1]; max = n[2]
        log <- apply(df.temp, 2, is.na)
        logindex <- apply(log, 1, function(x) {sum(x) >= min && sum(x) <= max})
        result = df[logindex, ]
    }

    return(result)
}

또한 complete.cases 확인하십시오.

> final[complete.cases(final), ]
             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0    2    2    2    2
6 ENSG00000221312    0    1    2    3    2

na.omit 은 모든 NA 제거하기에 더 na.omit . complete.cases 는 데이터 프레임의 특정 열만 포함하여 부분 선택을 허용합니다.

> final[complete.cases(final[ , 5:6]),]
             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0    2    2    2    2
4 ENSG00000207604    0   NA   NA    1    2
6 ENSG00000221312    0    1    2    3    2

솔루션이 작동하지 않습니다. is.na 를 사용하기를 원한다면 다음과 같이해야합니다 :

> final[rowSums(is.na(final[ , 5:6])) == 0, ]
             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0    2    2    2    2
4 ENSG00000207604    0   NA   NA    1    2
6 ENSG00000221312    0    1    2    3    2

그러나 complete.cases 사용하면 훨씬 더 명확하고 빠릅니다.


우리는 또한 이것을위한 부분 집합 함수를 사용할 수 있습니다.

finalData<-subset(data,!(is.na(data["mmul"]) | is.na(data["rnor"])))

이렇게하면 mmul과 rnor에 NA가없는 행만 표시됩니다.


파이프 ( %>% )를 좋아한다면 tidyr 의 새로운 drop_na 는 친구입니다.

library(tidyr)
df %>% drop_na()
#              gene hsap mmul mmus rnor cfam
# 2 ENSG00000199674    0    2    2    2    2
# 6 ENSG00000221312    0    1    2    3    2
df %>% drop_na(rnor, cfam)
#              gene hsap mmul mmus rnor cfam
# 2 ENSG00000199674    0    2    2    2    2
# 4 ENSG00000207604    0   NA   NA    1    2
# 6 ENSG00000221312    0    1    2    3    2

행에 NAs가 있는지 여부를 확인하는 방법을 선호합니다.

row.has.na <- apply(final, 1, function(x){any(is.na(x))})

행에 NA가 있는지 여부를 나타내는 값이있는 논리 벡터를 반환합니다. 이를 사용하여 삭제해야하는 행 수를 확인할 수 있습니다.

sum(row.has.na)

결국 떨어 뜨린다.

final.filtered <- final[!row.has.na,]

일부 NAs의 행을 필터링하는 경우 약간의 번거 로움이 있습니다 (예 : '최종 [, 5 : 6]'을 '적용'에 제공 할 수 있음). 일반적으로 Joris Meys의 솔루션은 좀 더 우아 해 보입니다.


dat 을 데이터 프레임으로 가정하면 예상되는 출력은 다음을 사용하여 얻을 수 있습니다.

1. rowSums

> dat[!rowSums((is.na(dat))),]
             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0   2    2    2    2
6 ENSG00000221312    0   1    2    3    2

2. lapply

> dat[!Reduce('|',lapply(dat,is.na)),]
             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0   2    2    2    2
6 ENSG00000221312    0   1    2    3    2

na.omit(your.data.frame) 시도하십시오. 두 번째 질문에 대해서는 다른 질문으로 게시하십시오 (명확성을 위해).





r-faq