Remover linhas com todos ou alguns NAs (valores ausentes) em data.frame

Gostaria de remover as linhas neste quadro de dados que:

a) conter NA em todas as colunas. Abaixo está meu quadro de dados de exemplo.

  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 

Basicamente, eu gostaria de obter um quadro de dados como o seguinte.

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

b) conter NA s em apenas algumas colunas , então eu também posso obter este resultado:

  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 

Verifique também o 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 é melhor apenas remover todos os NA ‘s. complete.cases permite a seleção parcial, incluindo apenas algumas colunas do dataframe:

 > 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 

Sua solução não pode funcionar. Se você insiste em usar is.na , então você tem que fazer algo como:

 > 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 

mas usar o complete.cases é bem mais claro e rápido.

Experimente na.omit(your.data.frame) . Quanto à segunda pergunta, tente postá-la como outra questão (para maior clareza).

Eu prefiro seguir o caminho para verificar se as linhas contêm algum NAs:

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

Isso retorna o vetor lógico com valores indicando se existe algum NA em uma linha. Você pode usá-lo para ver quantas linhas você terá que soltar:

 sum(row.has.na) 

e, eventualmente, soltá-los

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

Para filtrar linhas com certa parte de NAs, torna-se um pouco mais complicado (por exemplo, você pode alimentar 'final [, 5: 6]' para 'aplicar'). Geralmente, a solução de Joris Meys parece ser mais elegante.

Se você gosta de canos ( %>% ), o novo drop_na é seu amigo:

 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 

Outra opção se você quiser maior controle sobre como as linhas são consideradas inválidas é

 final < - final[!(is.na(final$rnor)) | !(is.na(rawdata$cfam)),] 

Usando o acima, isso:

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

Torna-se:

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

... onde apenas a linha 5 é removida, pois é a única linha contendo rnor para ambos rnor E cfam . A lógica booleana pode então ser alterada para atender a requisitos específicos.

Se você quiser controlar quantos NAs são válidos para cada linha, tente esta function. Para muitos conjuntos de dados de pesquisa, muitas respostas de perguntas em branco podem arruinar os resultados. Então, eles são excluídos após um certo limite. Esta function permitirá que você escolha quantos NAs a fila pode ter antes de ser deletada:

 delete.na < - function(DF, n=0) { DF[rowSums(is.na(DF)) <= n,] } 

Por padrão, eliminará todos os NAs:

 delete.na(final) gene hsap mmul mmus rnor cfam 2 ENSG00000199674 0 2 2 2 2 6 ENSG00000221312 0 1 2 3 2 

Ou especifique o número máximo de NAs permitido:

 delete.na(final, 2) 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 

Isso retornará as linhas que possuem pelo menos um valor não-NA.

 final[rowSums(is.na(final)) 

Isso retornará as linhas que possuem pelo menos DOIS valor não-NA.

 final[rowSums(is.na(final))< (length(final)-1),] 

Também podemos usar a function de subconjunto para isso.

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

Isso dará apenas as linhas que não possuem NA em mmul e rnor

Usando o pacote dplyr, podemos filtrar NA da seguinte forma:

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

Se performance for uma prioridade, use data.table e na.omit() com param cols= opcional cols= .

na.omit.data.table é o mais rápido no meu benchmark (veja abaixo), seja para todas as colunas ou para colunas selecionadas (parte 2 da questão OP).

Se você não quiser usar data.table , use complete.cases() .

Em um baunilha data.frame , complete.cases é mais rápido que na.omit() ou dplyr::drop_na() . Observe que na.omit.data.frame não suporta cols= .

Resultado de referência

Aqui está uma comparação dos methods base (azul), dplyr (rosa) e data.table (amarelo) para eliminar todas ou selecionar observações ausentes, no dataset nocional de 1 milhão de observações de 20 variables ​​numéricas com probabilidade independente de 5% de ser ausente e um subconjunto de 4 variables ​​para a parte 2.

Seus resultados podem variar com base no comprimento, na largura e na dispersão de seu dataset específico.

Observe a escala de log no eixo y.

insira a descrição da imagem aqui

Script de benchmark

 #------- 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% 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)) ) 

Para sua primeira pergunta, tenho um código com o qual me sinto à vontade para me livrar de todos os NAs. Obrigado pelo @Gregor para simplificar.

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

Para a segunda questão, o código é apenas uma alternativa da solução anterior.

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

Observe que o -5 é o número de colunas nos seus dados. Isso eliminará linhas com todos os NAs, já que o rowSums adiciona até 5 e eles se tornam zeros após a subtração. Desta vez, as.logical é necessário.

Eu sou um sintetizador :). Aqui eu combinei as respostas em uma function:

 #' 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://stackoverflow.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) } 

Assumindo dat como seu dataframe, a saída esperada pode ser obtida usando

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 
 delete.dirt < - function(DF, dart=c('NA')) { dirty_rows <- apply(DF, 1, function(r) !any(r %in% dart)) DF <- DF[dirty_rows, ] } mydata <- delete.dirt(mydata) 

A function acima exclui todas as linhas do quadro de dados que tem 'NA' em qualquer coluna e retorna os dados resultantes. Se você quiser verificar vários valores como NA e ? altere dart=c('NA') na function param para dart=c('NA', '?')

Meu palpite é que isso poderia ser mais elegantemente resolvido desta maneira

  m < - matrix(1:25, ncol = 5) m[c(1, 6, 13, 25)] <- NA df <- data.frame(m) library(dplyr) df %>% filter_all(any_vars(is.na(.))) #> X1 X2 X3 X4 X5 #> 1 NA NA 11 16 21 #> 2 3 8 NA 18 23 #> 3 5 10 15 20 NA