values - Eliminar filas con todos o algunos NA(valores perdidos) en data.frame




subset in r (10)

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

a) contienen NA s en todas las columnas. A continuación se muestra mi ejemplo de marco de datos.

             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

Si el rendimiento es una prioridad, use data.table y na.omit() con na.omit() opcionales 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 columnas seleccionadas (pregunta 2 de la parte OP).

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

En un data.frame vainilla, 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 (yellow) para data.table todas las observaciones faltantes o seleccionarlas, en un conjunto de datos nocionales de 1 millón de observaciones de 20 variables numéricas con un 5% de probabilidad independiente de ser faltan, y un subconjunto de 4 variables para la parte 2.

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

Observe la escala de registro en el eje y.

Guión 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))
)

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),]

Para su primera pregunta, tengo un código con el que me siento cómodo para deshacerme de todas las AN. Gracias por @Gregor para hacerlo más simple.

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

Para la segunda pregunta, el código es solo una alternativa 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 todos los NA, ya que rowSums suma hasta 5 y se convierten en ceros después de la resta. Esta vez, as.logical es necesario.


Prefiero la siguiente forma de verificar si las filas contienen 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 algún NA en una fila. Puedes usarlo para ver cuántas filas tendrás que soltar:

sum(row.has.na)

y finalmente soltarlos

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

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


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

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

Por defecto, eliminará todas las AN:

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 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

Si te gustan las tuberías ( %>% ), el 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

Supongo 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

Suponiendo dat sea ​​su marco de datos, el resultado esperado se puede lograr utilizando

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

También puedes ver las casillas 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 solo para eliminar todas las NA . complete.cases permite la selección parcial incluyendo solo ciertas columnas del marco de datos:

> 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 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 el uso de complete.cases es mucho más claro y más rápido.


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

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






r-faq