Eliminar filas con todas o algunas NA (valores faltantes) en data.frame

Me gustaría eliminar las líneas en este dataframe que:

a) contiene NA s en todas las columnas. A continuación está mi dataframe de ejemplo.

  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 

Básicamente, me gustaría obtener un dataframe como el siguiente.

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

b) contiene NA en solo algunas columnas , por lo que también puedo obtener 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 

También verifique 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 es más agradable simplemente por eliminar todos los NA ‘s. complete.cases permite la selección parcial al incluir solo ciertas columnas del 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 

Tu solución no puede funcionar Si insiste en usar is.na , entonces tiene que hacer 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 

pero usar complete.cases es bastante más claro y rápido.

Prueba na.omit(your.data.frame) . En cuanto a la segunda pregunta, intente publicarla como otra pregunta (para mayor claridad).

Prefiero seguir la forma de verificar si las filas contienen alguna NA:

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

Esto devuelve un vector lógico con valores que indican si hay alguna NA en una fila. Puedes usarlo para ver cuántas filas tendrás que soltar:

 sum(row.has.na) 

y eventualmente dejarlos caer

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

Para filtrar filas con cierta parte de NA, se vuelve un poco más complicado (por ejemplo, puede enviar 'final [, 5: 6]' a 'aplicar'). En general, la solución de Joris Meys parece ser más elegante.

Si te gustan las tuberías ( %>% ), tidyr ‘s new drop_na es tu 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 

Otra opción si desea un mayor control sobre cómo se considera que las filas son inválidas es

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

Usando lo anterior, esto:

  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 

Se convierte en:

  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 

... donde solo se elimina la fila 5, ya que es la única fila que contiene NA para rnor AND cfam . La lógica booleana se puede cambiar para ajustarse a requisitos específicos.

Si desea controlar cuántas NA son válidas para cada fila, pruebe esta función. Para muchos conjuntos de datos de encuestas, demasiadas respuestas a preguntas en blanco pueden arruinar los resultados. Entonces se eliminan después de un cierto umbral. Esta función le permitirá elegir cuántas NA puede tener la fila antes de ser eliminada:

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

Por defecto, eliminará todas las NA:

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

O especifique la cantidad máxima de NA permitidas:

 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 

Esto devolverá las filas que tienen al menos UN valor no NA.

 final[rowSums(is.na(final)) 

Esto devolverá las filas que tienen al menos DOS valores no NA.

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

También podemos usar la función de subconjunto para esto.

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

Esto dará solo aquellas filas que no tienen NA tanto en mmul como en rnor

Usando el paquete dplyr podemos filtrar NA de la siguiente manera:

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

Si el rendimiento es una prioridad, use data.table y na.omit() con param cols= opcional.

na.omit.data.table es el más rápido en mi punto de referencia (ver a continuación), ya sea para todas las columnas o para seleccionar columnas (pregunta OP parte 2).

Si no desea usar data.table , use complete.cases() .

En un vainilla data.frame , complete.cases es más rápido que na.omit() o dplyr::drop_na() . Tenga en cuenta que na.omit.data.frame no admite cols= .

Resultado de referencia

Aquí hay una comparación de los métodos base (azul), dplyr (rosa) y data.table (amarillo) para data.table todas o seleccionar observaciones faltantes, en el conjunto de datos nocionales de 1 millón de observaciones de 20 variables numéricas con 5% de probabilidad de ser independiente faltante, y un subconjunto de 4 variables para la parte 2.

Sus resultados pueden variar en función de la longitud, el ancho y la dispersión de su conjunto de datos en particular.

Note la escala de registro en el eje y.

enter image description here

Script de referencia

 #------- 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 su primera pregunta, tengo un código con el que me siento cómodo para deshacerme de todas las NA. Gracias por @Gregor para hacerlo más simple.

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

Para la segunda pregunta, el código es solo una alternancia de la solución anterior.

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

Observe que el -5 es el número de columnas en sus datos. Esto eliminará las filas con todas las NA, ya que las filas sum 5 y se convierten en ceros después de la resta. Esta vez, as.logical es necesario.

Soy un sintetizador :). Aquí combiné las respuestas en una función:

 #' 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) } 

Suponiendo dat como su dataframe, el resultado esperado se puede lograr 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) 

La función anterior borra todas las filas del dataframe que tiene 'NA' en cualquier columna y devuelve los datos resultantes. Si desea verificar valores múltiples como NA y ? cambiar dart=c('NA') en la función param a dart=c('NA', '?')

Mi suposición es que esto podría resolverse de forma más elegante de esta manera

  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