Elimine filas con todos o algunos NAS (valores faltantes) en los datos.marco


Me gustaría eliminar las líneas en este marco de datos que:

A) contiene NA s en todas las columnas. A continuación se muestra mi marco de datos 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 marco de datos 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 s solo en 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
Author: Jaap, 2011-02-01

15 answers

Compruebe tambiéncomplete.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 mejor simplemente eliminar todos los NA. complete.cases permite la selección parcial incluyendo 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

Su solución no puede funcionar. Si insistes en usar is.na, entonces tienes 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 mucho más claro y más rápido.

 845
Author: Joris Meys,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-14 15:10:51

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

 209
Author: Roman Luštrik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-01 12:00:44

Prefiero la siguiente forma de verificar si las filas contienen algún NAs:

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

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

sum(row.has.na)

Y finalmente dejarlos caer

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

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

 80
Author: donshikin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-02 21:58:33

Si te gustan las tuberías (%>%), tidyr's nuevo 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
 56
Author: lukeA,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-16 08:49:23

Otra opción si desea un mayor control sobre cómo se consideran inválidas las filas 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 NAs tanto para rnor COMO PARA cfam. La lógica booleana se puede cambiar para adaptarse a requisitos específicos.

 37
Author: getting-there,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-05 06:30:24

Si desea controlar cuántos NAS son válidos para cada fila, pruebe esta función. Para muchos conjuntos de datos de encuestas, demasiadas respuestas de preguntas en blanco pueden arruinar los resultados. Por lo tanto, se eliminan después de un cierto umbral. Esta función le permitirá elegir cuántos NAS puede tener la fila antes de eliminarla:

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

Por defecto, eliminará todos los NAs:

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 el 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,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-18 13:59:11

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

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

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

final[rowSums(is.na(final))<(length(final)-1),]
 14
Author: Leo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-19 14:39:08

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

dplyr::filter(df,  !is.na(columnname))
 14
Author: Raminsu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-12 05:44:40

También podemos usar la función 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

 12
Author: Ramya Ural,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-11 22:20:38

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

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

Si no quieres usar data.table, usa complete.cases().

En una de vainilla data.frame, complete.cases es más rápido que na.omit() o dplyr::drop_na(). Observe que na.omit.data.frame no es compatible con cols=.

Resultado de referencia

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

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

Observe la escala logarítmica en el eje y.

introduzca la descripción de la imagen aquí

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<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,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-16 15:47:40

Para su primera pregunta, tengo un código con el que me siento cómodo para deshacerme de todos los NAS. 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 -5 es el número de columnas en sus datos. Esto eliminará las filas con todos los NAS, ya que los números de fila suman 5 y se convierten en ceros después de restar. Esta vez, como.lógico es necesario.

 11
Author: LegitMe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-07 14:57:40

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)
}
 8
Author: Jerry T,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-10 18:26:40

Asumiendo dat como su dataframe, la salida esperada 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
 5
Author: Prradep,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
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)

La función Above elimina todas las filas del marco de datos que tiene 'NA' en cualquier columna y devuelve los datos resultantes. Si desea verificar múltiples valores como NA y ? cambie dart=c('NA') en el parámetro de función a dart=c('NA', '?')

 1
Author: sapy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-22 22:19:46

Mi conjetura es que esto podría resolverse de manera 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
 0
Author: Joni Hoppen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-08 20:35:47