Cambie las celdas en blanco a "NA"


79

Aquí está el enlace de mis datos.

Mi objetivo es asignar "NA" a todas las celdas en blanco independientemente de los valores categóricos o numéricos. Estoy usando na.strings = "" . Pero no asigna NA a todas las celdas en blanco.

## reading the data
dat <- read.csv("data2.csv")
head(dat)
  mon hr        acc   alc sex spd axles door  reg                                 cond1 drug1
1   8 21 No Control  TRUE   F   0     2    2      Physical Impairment (Eyes, Ear, Limb)     A
2   7 20 No Control FALSE   M 900     2    2                                Inattentive     D
3   3  9 No Control FALSE   F 100     2    2 2004                                Normal     D
4   1 15 No Control FALSE   M   0     2    2      Physical Impairment (Eyes, Ear, Limb)     D
5   4 21 No Control FALSE      25    NA   NA                                                D
6   4 20 No Control    NA   F  30     2    4                Drinking Alcohol - Impaired     D
       inj1 PED_STATE st rac1
1     Fatal      <NA>  F <NA>
2  Moderate      <NA>  F <NA>
3  Moderate      <NA>  M <NA>
4 Complaint      <NA>  M <NA>
5 Complaint      <NA>  F <NA>
6  Moderate      <NA>  M <NA>


## using na.strings
dat2 <- read.csv("data2.csv", header=T, na.strings="")
head(dat2)
  mon hr        acc   alc sex spd axles door  reg                                 cond1 drug1
1   8 21 No Control  TRUE   F   0     2    2 <NA> Physical Impairment (Eyes, Ear, Limb)     A
2   7 20 No Control FALSE   M 900     2    2 <NA>                           Inattentive     D
3   3  9 No Control FALSE   F 100     2    2 2004                                Normal     D
4   1 15 No Control FALSE   M   0     2    2 <NA> Physical Impairment (Eyes, Ear, Limb)     D
5   4 21 No Control FALSE      25    NA   NA <NA>                                  <NA>     D
6   4 20 No Control    NA   F  30     2    4 <NA>           Drinking Alcohol - Impaired     D
       inj1 PED_STATE st rac1
1     Fatal        NA  F   NA
2  Moderate        NA  F   NA
3  Moderate        NA  M   NA
4 Complaint        NA  M   NA
5 Complaint        NA  F   NA
6  Moderate        NA  M   NA

Utilice texto, no imágenes / enlaces, para el texto, incluidas tablas y ERD. Parafrasea o cita de otro texto. Utilice imágenes solo para lo que no se pueda expresar como texto o para aumentar el texto. Las imágenes no se pueden buscar ni cortar y pegar. Incluya una leyenda / clave y una explicación con una imagen. Haz que tu publicación sea autónoma. Inserte imágenes / enlaces usando funciones de edición.
philipxy

Respuestas:


97

Supongo que está hablando de la columna "sexo" de la fila 5. Podría darse el caso de que en el archivo data2.csv, la celda contenga un espacio y, por lo tanto, R. no la considere vacía.

Además, noté que en la fila 5 columnas "ejes" y "puerta", los valores originales leídos de data2.csv son la cadena "NA". Probablemente también desee tratarlos como na.strings. Para hacer esto,

dat2 <- read.csv("data2.csv", header=T, na.strings=c("","NA"))

EDITAR:

Descargué su data2.csv. Sí, hay un espacio en la columna "sexo" de la fila 5. Entonces quieres

na.strings=c(""," ","NA")

34

Puede usar gsub para reemplazar múltiples mutaciones de vacío, como "" o un espacio, para que sea NA:

data= data.frame(cats=c('', ' ', 'meow'), dogs=c("woof", " ", NA))
apply(data, 2, function(x) gsub("^$|^ $", NA, x))

2
También se puede utilizar gsub("^$", NA, trimws(x))para manejar más de un espacio dentro de una celda. Sin embargo, tenga cuidado con ambos enfoques que convierten todas las columnas en variables de cadena / carácter (si aún no lo ha hecho).
JWilliman

27

Una solución más amigable para la vista dplyrsería

require(dplyr)

## fake blank cells
iris[1,1]=""

## define a helper function
empty_as_na <- function(x){
    if("factor" %in% class(x)) x <- as.character(x) ## since ifelse wont work with factors
    ifelse(as.character(x)!="", x, NA)
}

## transform all columns
iris %>% mutate_each(funs(empty_as_na)) 

Para aplicar la corrección a solo un subconjunto de columnas, puede especificar columnas de interés utilizando la sintaxis de coincidencia de columnas de dplyr. Ejemplo:mutate_each(funs(empty_as_na), matches("Width"), Species)

En caso de que su tabla contenga fechas, debería considerar usar una versión más segura de tipos deifelse


11
¿Cómo es que agregar una nueva biblioteca, crear una nueva función es más amigable para la vista ? Y creo que lo necesitarás ifelse(x %in% c(""," ","NA"), NA, x).
zx8754

4
El uso de una función junto con mutate_eachproporciona más flexibilidad y un patrón reutilizable. dplyres omnipresente en los flujos de trabajo R actuales y se acaba de agregar para que la respuesta sea autónoma. Creo que x!=""es correcto aquí, ya que ni "" ni "NA" están en blanco. Además, la respuesta de @ sclarky falla para los marcos de datos que contienen números, y la de @ Badoe realmente no resuelve el problema para los marcos de datos existentes, por lo que ninguna otra respuesta parece responder la pregunta todavía de una manera genérica. Estoy feliz de conocer mejores soluciones.
Holger Brandl

1
dplyr es omnipresente en los flujos de trabajo R actuales , no, no lo es. ¿Y qué significa " y @ Badoe's no resuelve realmente el problema de los marcos de datos existentes "? ¿Puede ampliar un poco esa afirmación?
David Arenburg

10
Badoe detalla cómo configurar read.csvpara transformar celdas en blanco a NA al leer una tabla de un archivo. Sin embargo, dado que el título de la pregunta es "Cambiar las celdas en blanco a" NA "", una respuesta completa debería cubrir la situación en la que un data.frame ya está en el entorno y el usuario quiere deshacerse de las celdas en blanco.
Holger Brandl

1
Puede que esto no sea lo que estaba buscando el OP, pero me ayudó a contar los valores faltantes, incluidas las cadenas vacías y NA. df %>% mutate_all(funs(empty_as_na)) %>% summarize_all(funs(sum(is.na(.))))Si bien dplyr puede o no tener una adopción generalizada, goza de popularidad en un gran subconjunto de usuarios de R, incluido yo, así que gracias por esta solución.
Dannid

22

Esto debería funcionar

dat <- dat %>% mutate_all(na_if,"")

1
Intenté eso en un objeto sf, y arrojó un error de análisis: tipo WKB desconocido 12. Parece mutate intentó reemplazar algo en las geometrías.
aae

15

Recientemente me encontré con problemas similares, y esto es lo que funcionó para mí.

Si la variable es numérica, una simple df$Var[df$Var == ""] <- NAdebería ser suficiente. Pero si la variable es un factor, primero debe convertirlo al carácter, luego reemplazar las ""celdas con el valor que desea y convertirlo nuevamente en factor. Entonces, por ejemplo, su Sexvariable, supongo que sería un factor y si desea reemplazar la celda vacía, haría lo siguiente:

df$Var <- as.character(df$Var)
df$Var[df$Var==""] <- NA
df$Var <- as.factor(df$Var)

3

Mi función tiene en cuenta el factor, el vector de caracteres y los atributos potenciales, si usa haven o un paquete externo para leer archivos externos. También permite hacer coincidir diferentes na.strings autodefinidos. Para transformar todas las columnas, simplemente use lappy:df[] = lapply(df, blank2na, na.strings=c('','NA','na','N/A','n/a','NaN','nan'))

Ver más los comentarios:

#' Replaces blank-ish elements of a factor or character vector to NA
#' @description Replaces blank-ish elements of a factor or character vector to NA
#' @param x a vector of factor or character or any type
#' @param na.strings case sensitive strings that will be coverted to NA. The function will do a trimws(x,'both') before conversion. If NULL, do only trimws, no conversion to NA.
#' @return Returns a vector trimws (always for factor, character) and NA converted (if matching na.strings). Attributes will also be kept ('label','labels', 'value.labels').
#' @seealso \code{\link{ez.nan2na}}
#' @export
blank2na = function(x,na.strings=c('','.','NA','na','N/A','n/a','NaN','nan')) {
    if (is.factor(x)) {
        lab = attr(x, 'label', exact = T)
        labs1 <- attr(x, 'labels', exact = T)
        labs2 <- attr(x, 'value.labels', exact = T)

        # trimws will convert factor to character
        x = trimws(x,'both')
        if (! is.null(lab)) lab = trimws(lab,'both')
        if (! is.null(labs1)) labs1 = trimws(labs1,'both')
        if (! is.null(labs2)) labs2 = trimws(labs2,'both')

        if (!is.null(na.strings)) {
            # convert to NA
            x[x %in% na.strings] = NA
            # also remember to remove na.strings from value labels 
            labs1 = labs1[! labs1 %in% na.strings]
            labs2 = labs2[! labs2 %in% na.strings]
        }

        # the levels will be reset here
        x = factor(x)

        if (! is.null(lab)) attr(x, 'label') <- lab
        if (! is.null(labs1)) attr(x, 'labels') <- labs1
        if (! is.null(labs2)) attr(x, 'value.labels') <- labs2
    } else if (is.character(x)) {
        lab = attr(x, 'label', exact = T)
        labs1 <- attr(x, 'labels', exact = T)
        labs2 <- attr(x, 'value.labels', exact = T)

        # trimws will convert factor to character
        x = trimws(x,'both')
        if (! is.null(lab)) lab = trimws(lab,'both')
        if (! is.null(labs1)) labs1 = trimws(labs1,'both')
        if (! is.null(labs2)) labs2 = trimws(labs2,'both')

        if (!is.null(na.strings)) {
            # convert to NA
            x[x %in% na.strings] = NA
            # also remember to remove na.strings from value labels 
            labs1 = labs1[! labs1 %in% na.strings]
            labs2 = labs2[! labs2 %in% na.strings]
        }

        if (! is.null(lab)) attr(x, 'label') <- lab
        if (! is.null(labs1)) attr(x, 'labels') <- labs1
        if (! is.null(labs2)) attr(x, 'value.labels') <- labs2
    } else {
        x = x
    }
    return(x)
}

3

También puede utilizar mutate_atendplyr

dat <- dat %>%
mutate_at(vars(colnames(.)),
        .funs = funs(ifelse(.=="", NA, as.character(.))))

Seleccione columnas individuales para cambiar:

dat <- dat %>%
mutate_at(vars(colnames(.)[names(.) %in% c("Age","Gender")]),
        .funs = funs(ifelse(.=="", NA, as.character(.))))

A partir de (dplyr 0.8.0 anterior), la forma en que debe escribirse ha cambiado. Antes de que fuera, funs()en .funs (funs(name = f(.)). En lugar de funs, ahora usamoslist (list(name = ~f(.)))

Tenga en cuenta que también hay una forma mucho más sencilla de enumerar los nombres de las columnas. (tanto el nombre de la columna como el índice de la columna funcionan)

dat <- dat %>%
mutate_at(.vars = c("Age","Gender"),
    .funs = list(~ifelse(.=="", NA, as.character(.))))

2

Si bien muchas de las opciones anteriores funcionan bien, encontré que la coerción de variables no objetivo es chrproblemática. Usar ifelsey grepldentro lapplyresuelve este efecto fuera del objetivo (en pruebas limitadas). Usando la expresión regular de slarky en grepl:

set.seed(42)
x1 <- sample(c("a","b"," ", "a a", NA), 10, TRUE)
x2 <- sample(c(rnorm(length(x1),0, 1), NA), length(x1), TRUE)

df <- data.frame(x1, x2, stringsAsFactors = FALSE)

El problema de la coacción a la clase de personaje:

df2 <- lapply(df, function(x) gsub("^$|^ $", NA, x))
lapply(df2, class)

PSx1 [1] "personaje"

$ x2 [1] "personaje"

Resolución con el uso de ifelse:

df3 <- lapply(df, function(x) ifelse(grepl("^$|^ $", x)==TRUE, NA, x))
lapply(df3, class)

PSx1 [1] "personaje"

$ x2 [1] "numérico"


2

Sospecho que todos ya tienen una respuesta, aunque en caso de que alguien venga a mirar, dplyr na_if () sería (desde mi perspectiva) el más eficiente de los mencionados:

# Import CSV, convert all 'blank' cells to NA
dat <- read.csv("data2.csv") %>% na_if("")

Aquí hay un enfoque adicional que aprovecha la función read_delim de readr. Acabo de recoger (probablemente lo sepa, pero lo archivaré aquí para futuros usuarios). Esto es muy sencillo y más versátil que lo anterior, ya que puede capturar todo tipo de valores en blanco y relacionados con NA en su archivo csv:

dat <- read_csv("data2.csv", na = c("", "NA", "N/A"))

Tenga en cuenta el subrayado en la versión del lector frente a Base R "." en read_csv.

¡Ojalá esto ayude a alguien que deambula por la publicación!


0

¿No podrías simplemente usar

dat <- read.csv("data2.csv",na.strings=" ",header=TRUE)

debe convertir todos los espacios en blanco a NA a medida que se leen los datos, asegúrese de dejar un espacio entre la cita


¿Qué pasa si no pones el espacio entre las citas?
Nneka

0

Para aquellos que se preguntan acerca de una solución usando la forma data.table , aquí hay una para la que escribí una función, disponible en mi Github:

library(devtools)
source_url("https://github.com/YoannPa/Miscellaneous/blob/master/datatable_pattern_substitution.R?raw=TRUE")
dt.sub(DT = dat2, pattern = "^$|^ $",replacement = NA)
dat2

La función recorre cada columna para identificar qué columna contiene coincidencias de patrones. Luego gsub()se aplica solo en columnas que contienen coincidencias para el patrón "^$|^ $", para sustituir coincidencias porNA s.

Seguiré mejorando esta función para que sea más rápida.


0

Esto funciona para mi.

dataset <- read.csv(file = "data.csv",header=TRUE,fill = T,na.strings = "")

-3

Llame al dplyrpaquete instalando desde cranen r

library(dplyr)

(file)$(colname)<-sub("-",NA,file$colname) 

Convertirá toda la celda en blanco en una columna en particular como NA

Si la columna contiene "-", "", 0 como este, cámbielo en el código de acuerdo con el tipo de celda en blanco

Por ejemplo, si obtengo una celda en blanco como "" en lugar de "-", entonces use este código:

(file)$(colname)<-sub("", NA, file$colname)

1
Esta respuesta no se usa dplyrdespués de cargarla y no se escala bien a "todas las columnas" como OP está buscando.
Gregor Thomas
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.