Manera elegante de informar valores faltantes en un marco de datos

80

Aquí hay un pequeño fragmento de código que escribí para reportar variables con valores perdidos de un marco de datos. Estoy tratando de pensar en una forma más elegante de hacer esto, una que quizás devuelva un data.frame, pero estoy atascado:

for (Var in names(airquality)) {
    missing <- sum(is.na(airquality[,Var]))
    if (missing > 0) {
        print(c(Var,missing))
    }
}

Editar: estoy tratando con data.frames con decenas a cientos de variables, por lo que es clave que solo informemos las variables con valores perdidos.

Zach
fuente
@kohske: ese fue mi primer pensamiento, pero los resultados son una serie tablede caracteres y tendrías que analizar la cantidad de NA.
Joshua Ulrich
Revertiré su pregunta, ya que publicó una respuesta. Si desea comentar una respuesta, hágalo como comentario a esa respuesta. Si las preguntas también contienen respuestas, esto se vuelve muy confuso.
Andrie
@Andrie: No estoy de acuerdo con su edición, ya que un problema clave al que me enfrento es informar solo las variables con valores perdidos. Además, su reversión eliminó un cambio que hice en el código. Edité mi pregunta para incluir esta información y agregué mi versión modificada del código de Josh a un comentario.
Zach
@Zach Tu nueva edición me parece bien. No soy reacio a agregar datos / solicitudes adicionales en una pregunta una vez que esté activa, por cierto, si esto aclara la pregunta.
Andrie
Hay medio millón de formas de hacer esto, consulte Vista de tareas de CRAN - MissingData
zx8754

Respuestas:

156

Solo usa sapply

> sapply(airquality, function(x) sum(is.na(x)))
  Ozone Solar.R    Wind    Temp   Month     Day 
     37       7       0       0       0       0

También puede usar applyo colSumsen la matriz creada poris.na()

> apply(is.na(airquality),2,sum)
  Ozone Solar.R    Wind    Temp   Month     Day 
     37       7       0       0       0       0
> colSums(is.na(airquality))
  Ozone Solar.R    Wind    Temp   Month     Day 
     37       7       0       0       0       0 
Joshua Ulrich
fuente
12
Modifiqué su código ligeramente, para informar solo el valor faltante:M <- sapply(airquality, function(x) sum(is.na(x))); M[M>0]
Zach
¡Gracias! Aprendió mucho.
Bombyx mori
Hola @Joshua Ulrich, muchas gracias por tu código sucinto. Me gustaría agregar una columna en el marco de datos que mostraría el porcentaje de valores na. ¿Puede ayudarme con esto?
DukeLover
2
@Zach Utilizo una versión de su sugerencia para verificar que los campos obligatorios tengan un valor:M <- colSums(is.na(airquality)); M[M <= 0]
Anthony Simon Mielniczuk
@Joshua ¡agregar una opción para% s también sería genial!
radek
8

Podemos usar map_dfcon purrr.

library(mice)
library(purrr)

# map_df with purrr
map_df(airquality, function(x) sum(is.na(x)))
# A tibble: 1 × 6
# Ozone Solar.R  Wind  Temp Month   Day
# <int>   <int> <int> <int> <int> <int>
# 1    37       7     0     0     0     0
Keiku
fuente
1
¿Cuál es la ventaja de map_dfover sapply?
Zach
1
@Zach Creo que no hay una gran diferencia, pero Hadley dijo que no use sapply () dentro de una función. Consulte Excepciones y depuración · Advanced R. adv-r.had.co.nz/Exceptions-Debugging.html .
Keiku
para las personas perezosas como yo, puede escribir el código anterior en una sintaxis purrr más corta para funciones (~) para que se vea así:map_df( air quality, ~sum(is.na(.) )
Agile Bean
1
@Zach, la ventaja de map_dfover sapplyes solo cuando el resultado tiene muchas filas, ya que el formato de salida de map_df siempre es tibble.
Agile Bean
1
@Zach: es mejor usar vapplyvs sapplyen funciones porque vapplyle brinda una estructura de resultado conocida (que usted especifica). sapplypuede devolver una matriz o una lista, dependiendo de la salida de la función. Una desventaja de map_dfes que le da un data.frame como entrada, y devuelve una subclase data.frame, no un data.frame. No hay garantía de que tibbles se comporte como lo hacen data.frames en todos los casos necesarios en el futuro.
Joshua Ulrich
8

Mi nuevo favorito para los datos (no demasiado amplios) son los métodos del excelente paquete naniar . No solo obtienes frecuencias, sino también patrones de ausencia:

library(naniar)
library(UpSetR)

riskfactors %>%
  as_shadow_upset() %>%
  upset()

ingrese la descripción de la imagen aquí

A menudo es útil ver dónde están las faltas en relación con las que no faltan, lo que se puede lograr trazando un diagrama de dispersión con faltas:

ggplot(airquality,
       aes(x = Ozone,
           y = Solar.R)) +
 geom_miss_point()

ingrese la descripción de la imagen aquí

O para variables categóricas:

gg_miss_fct(x = riskfactors, fct = marital)

ingrese la descripción de la imagen aquí

Estos ejemplos son de la viñeta del paquete que enumera otras visualizaciones interesantes.

radek
fuente
2
¡Gracias por publicar esto! Ahora hay una gg_miss_upset()función dedicada en la última versión, que se enviará a CRAN una vez que regresen de las vacaciones. naniar.njtierney.com/reference/gg_miss_upset.html
Nick Tierney
6
summary(airquality)

ya te da esta informacion

Los paquetes VIM también ofrecen un buen diagrama de datos faltantes para data.frame

library("VIM")
aggr(airquality)

ingrese la descripción de la imagen aquí

Steffen Moritz
fuente
¿Puede el paquete VIM informar qué observaciones específicas tienen datos faltantes?
Anthony Simon Mielniczuk
no lo creo ... pero puede hacerlo bastante fácil (tendría que reemplazar airquality con su propio marco de datos): res <- airquality [rowSums (is.na (airquality))> 0,]
Steffen Moritz
4

Más sucinto: sum(is.na(x[1]))

Es decir

  1. x[1] Mira la primera columna

  2. is.na() cierto si es NA

  3. sum() TRUEes 1, FALSEes0

Keith Whittingham
fuente
esto no responde a la pregunta original, que es encontrar el número de NAs para todas las columnas en los datos
Ben Bolker
4

Otra alternativa gráfica: plot_missingfunción de un DataExplorerpaquete excelente :

ingrese la descripción de la imagen aquí

Docs también señala el hecho de que puede guardar estos resultados para un análisis adicional con missing_data <- plot_missing(data).

radek
fuente
La plot_missing()función en el DataExplorerpaquete es ahora PlotMissing().
coip
1
@coip PlotMissing()está obsoleto. Utilice en su plot_missing()lugar. Consulte el n. ° 49 para obtener más detalles.
Boxuan
2

Otra función que lo ayudaría a ver los datos faltantes sería df_status de la biblioteca funModeling

library(funModeling)

iris.2 es el conjunto de datos de iris con algunos NA añadidos. Puede reemplazarlo con su conjunto de datos.

df_status(iris.2)

Esto le dará el número y porcentaje de NA en cada columna.

Shahan Degamwala
fuente
1

Para una solución gráfica más, el visdat paquete ofrece vis_miss.

library(visdat)
vis_miss(airquality)

ingrese la descripción de la imagen aquí

Muy similar a la Ameliasalida con una pequeña diferencia de dar% s en las faltas de la caja.

radek
fuente
1

Creo que la biblioteca Amelia hace un buen trabajo en el manejo de los datos faltantes y también incluye un mapa para visualizar las filas faltantes.

install.packages("Amelia")
library(Amelia)
missmap(airquality)

ingrese la descripción de la imagen aquí

También puede ejecutar el siguiente código que devolverá los valores lógicos de na

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

Otra forma gráfica e interactiva es usar la is.na10función de la heatmaplybiblioteca:

library(heatmaply)

heatmaply(is.na10(airquality), grid_gap = 1, 
          showticklabels = c(T,F),
            k_col =3, k_row = 3,
            margins = c(55, 30), 
            colors = c("grey80", "grey20"))

ingrese la descripción de la imagen aquí

Probablemente no funcione bien con grandes conjuntos de datos.

radek
fuente
0

Si desea hacerlo para una columna en particular, también puede usar este

length(which(is.na(airquality[1])==T))
Chintak Chhapia
fuente
4
No es necesario comparar un vector lógico con T. También puede contar el número de elementos VERDADEROS en un vector lógico al sumarlo.
Houshalter
0

Una dplyrsolución para obtener el recuento podría ser:

summarise_all(df, ~sum(is.na(.)))

O para obtener un porcentaje:

summarise_all(df, ~(sum(is_missing(.) / nrow(df))))

Quizás también valga la pena señalar que los datos faltantes pueden ser desagradables, inconsistentes y no siempre están codificados NAsegún la fuente o cómo se manejan cuando se importan. La siguiente función podría modificarse según sus datos y lo que desee considerar que falta:

is_missing <- function(x){
  missing_strs <- c('', 'null', 'na', 'nan', 'inf', '-inf', '-9', 'unknown', 'missing')
  ifelse((is.na(x) | is.nan(x) | is.infinite(x)), TRUE,
         ifelse(trimws(tolower(x)) %in% missing_strs, TRUE, FALSE))
}

# sample ugly data
df <- data.frame(a = c(NA, '1', '  ', 'missing'),
                 b = c(0, 2, NaN, 4),
                 c = c('NA', 'b', '-9', 'null'),
                 d = 1:4,
                 e = c(1, Inf, -Inf, 0))

# counts:
> summarise_all(df, ~sum(is_missing(.)))
  a b c d e
1 3 1 3 0 2

# percentage:
> summarise_all(df, ~(sum(is_missing(.) / nrow(df))))
     a    b    c d   e
1 0.75 0.25 0.75 0 0.5
sbha
fuente