Remover as linhas com todos ou alguns NAs (valores em falta) nos dados.quadro

Eu gostaria de remover as linhas nesta moldura de dados que:

a) contém NA s em todas as colunas. abaixo está a minha moldura 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, 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) contém NA s apenas em algumas colunas , por isso também consigo 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
Author: Jaap, 2011-02-01

15 answers

Verifique também 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 é mais agradável para apenas remover todos os NA's. complete.cases permite a selecção parcial, incluindo apenas algumas colunas da 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
A tua 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 complete.cases é muito mais claro, e mais rápido.

 845
Author: Joris Meys, 2017-06-14 15:10:51

Tenta {[[0]}. Quanto à segunda questão, tente publicá-la como outra questão (para maior clareza).

 209
Author: Roman Luštrik, 2011-02-01 12:00:44

Prefiro a seguinte forma de verificar se as linhas contêm algum NAs:

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

Isto devolve um vector lógico com valores que indicam se existe alguma NA em linha. Você pode usá-lo para ver quantas linhas você vai ter que largar:

sum(row.has.na)

E eventualmente largá-los

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

Para filtrar linhas com determinada parte do 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.

 80
Author: donshikin, 2011-02-02 21:58:33

Se gostas de canos (%>%), tidyr's novo drop_na o 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
 56
Author: lukeA, 2016-08-16 08:49:23

Outra opção se quiser um maior controlo sobre como as linhas são consideradas inválidas é

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

Usando o acima, isto:

             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

...em que apenas a linha 5 é removida, uma vez que é a única linha que contém NAs para rnor e cfam. A lógica booleana pode então ser alterada para caber requisitos específicos.

 37
Author: getting-there, 2013-11-05 06:30:24

Se quiser controlar quantos NAs são válidos para cada linha, tente esta função. Para muitos conjuntos de dados de pesquisa, muitas respostas de perguntas em branco podem arruinar os resultados. Por isso, são apagados após um certo limiar. Esta função permitir-lhe-á escolher quantos NAs A linha pode ter antes de ser apagada:

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

Por defeito, irá 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 especificar 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
 31
Author: Pierre Lafortune, 2016-11-18 13:59:11

Isto irá devolver as linhas que têm pelo menos um valor não-NA.

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

Isto irá devolver as linhas que têm pelo menos dois valores não-NA.

final[rowSums(is.na(final))<(length(final)-1),]
 14
Author: Leo, 2014-09-19 14:39:08

Usando o pacote dplyr podemos filtrar NA do seguinte modo:

dplyr::filter(df,  !is.na(columnname))
 14
Author: Raminsu, 2017-04-12 05:44:40

Também podemos usar a função subconjunto para isto.

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

Isto dará apenas as linhas que não têm NA tanto em mmul como em rnor

 12
Author: Ramya Ural, 2014-11-11 22:20:38

Se o desempenho for prioritário, utilizar data.table e {[2] } com um param cols= opcional.

na.omit.data.table é o mais rápido do meu benchmark (ver abaixo), seja para todas as colunas ou para colunas selecionadas (op pergunta parte 2).

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

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

Resultado de referência

Aqui está um comparação dos métodos de base (Azul), dplyr (rosa) e data.table (amarelo) para deixar cair todas ou seleccionar observações em falta, num conjunto de dados nocionais de 1 milhão de observações de 20 variáveis numéricas com uma probabilidade independente de 5% de falta e um subconjunto de 4 variáveis para a parte 2.

Os seus resultados podem variar com base no comprimento, largura e escassez do seu conjunto de dados em particular.

Escala de Registo de notas no eixo Y.

enter image description here

Parâmetro de referência script

#-------  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))
)
 11
Author: C8H10N4O2, 2018-02-16 15:47:40
Para a tua primeira pergunta, tenho um código com o qual me sinto confortável para me livrar de todos os NAs. Obrigado por @Gregor para torná-lo mais simples.
final[!(rowSums(is.na(final))),]

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

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

Repare que -5 é o número de colunas nos seus dados. Isto irá eliminar as linhas com todos os NAs, uma vez que os rowSums totalizam 5 e tornam-se zeros após a subtração. Desta vez, as.a lógica é necessária.

 11
Author: LegitMe, 2018-03-07 14:57:40

Eu sou um sintetizador:). Aqui eu combinei as respostas em uma função:

#' 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)
}
 8
Author: Jerry T, 2016-12-10 18:26:40

Assumindo dat como o seu nome de dados, o resultado esperado pode ser alcançado 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
 5
Author: Prradep, 2017-03-15 16:51:32
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)

Acima da função apaga todas as linhas da moldura de dados que tem 'NA' em qualquer coluna e devolve os dados resultantes. Se quiser verificar se existem vários valores como NA e ?, Altere dart=c('NA') na função param para dart=c('NA', '?')

 1
Author: sapy, 2018-02-22 22:19:46
O meu palpite é que isto poderia ser resolvido de forma mais elegante.
  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
 0
Author: Joni Hoppen, 2018-05-08 20:35:47