subconjunto de una tabla de datos usando! = excluye a NA también

Tengo una tabla de datos con una columna que tiene NA . Quiero soltar filas donde esa columna toma un valor particular (que pasa a ser "" ). Sin embargo, mi primer bash me llevó a perder filas con NA ‘s también:

 > a = c(1,"",NA) > x  y <- x[a!=""];y a 1: 1 

Después de mirar ?`!=` , Encontré un trazador de líneas que funciona, pero es un dolor:

 > z <- x[!sapply(a,function(x)identical(x,""))]; z a 1: 1 2: NA 

Me pregunto si hay una mejor manera de hacer esto. Además, no veo una buena manera de extender esto para excluir múltiples valores no NA . Aquí hay una mala manera:

 > drop_these <- function(these,where){ + argh <- !sapply(where, + function(x)unlist(lapply(as.list(these),function(this)identical(x,this))) + ) + if (is.matrix(argh)){argh  x[drop_these("",a)] a 1: 1 2: NA > x[drop_these(c(1,""),a)] a 1: NA 

Miré a ?J y probé cosas con un dataframe, que parece funcionar de manera diferente, manteniendo las NA al subconjunto:

 > w <- data.frame(a,stringsAsFactors=F); w a 1 1 2 3  > d <- w[a!="",,drop=F]; d a 1 1 NA  

Para proporcionar una solución a su pregunta:

Deberías usar %in% . Te devuelve un vector lógico.

 a %in% "" # [1] FALSE TRUE FALSE x[!a %in% ""] # a # 1: 1 # 2: NA 

Para descubrir por qué sucede esto en data.table :

(como opuesto a data.frame )

Si observa el código fuente de data.table en el archivo data.table.R bajo la función "[.data.table" , hay un conjunto de if-statements que verifican i argumento. Uno de ellos es:

 if (!missing(i)) { # Part (1) isub = substitute(i) # Part (2) if (is.call(isub) && isub[[1L]] == as.name("!")) { notjoin = TRUE if (!missingnomatch) stop("not-join '!' prefix is present on i but nomatch is provided. Please remove nomatch."); nomatch = 0L isub = isub[[2L]] } ..... # "isub" is being evaluated using "eval" to result in a logical vector # Part 3 if (is.logical(i)) { # see DT[NA] thread re recycling of NA logical if (identical(i,NA)) i = NA_integer_ # avoids DT[!is.na(ColA) & !is.na(ColB) & ColA==ColB], just DT[ColA==ColB] else i[is.na(i)] = FALSE } .... } 

Para explicar la discrepancia, he pegado la parte importante del código aquí. Y también los he marcado en 3 partes.

Primero, ¿por qué dt[a != ""] No funciona como se esperaba (por el OP)?

Primero, la part 1 evalúa a un objeto de call de clase. La segunda parte de la statement if en la part 2 devuelve FALSE. Después de eso, la call se “evalúa” para dar c(TRUE, FALSE, NA) . Luego se ejecuta la part 3 . Entonces, NA se reemplaza a FALSE (la última línea del bucle lógico).

¿Por qué x[!(a== "")] funciona como se espera (por OP)?

part 1 devuelve una llamada una vez más. Pero, la part 2 evalúa como VERDADERO y por lo tanto establece:

 1) `notjoin = TRUE` 2) isub < - isub[[2L]] # which is equal to (a == "") without the ! (exclamation) 

Ahí es donde sucedió la magia. La negación ha sido eliminada por ahora. Y recuerda, este sigue siendo un objeto de llamada de clase. Entonces esto se evalúa (usando eval ) a lógica nuevamente. Entonces, (a=="") evalúa a c(FALSE, TRUE, NA) .

Ahora, esto se verifica para is.logical en la part 3 . Entonces, aquí, NA se reemplaza a FALSE . Por lo tanto, se convierte en c(FALSE, TRUE, FALSE) . En algún momento posterior, se ejecuta un which(c(F,T,F)) , que resulta en 2 aquí. Porque notjoin = TRUE (de la part 2 ) seq_len(nrow(x))[-2] = c (1,3) se devuelve. entonces, x[!(a=="")] básicamente devuelve x[c(1,3)] que es el resultado deseado. Aquí está el fragmento de código relevante:

 if (notjoin) { if (bywithoutby || !is.integer(irows) || is.na(nomatch)) stop("Internal error: notjoin but bywithoutby or !integer or nomatch==NA") irows = irows[irows!=0L] # WHERE MAGIC HAPPENS (returns c(1,3)) i = irows = if (length(irows)) seq_len(nrow(x))[-irows] else NULL # NULL meaning all rows ie seq_len(nrow(x)) # Doing this once here, helps speed later when repeatedly subsetting each column. R's [irows] would do this for each # column when irows contains negatives. } 

Dado que, creo que hay algunas inconsistencias con la syntax ... Y si consigo tener tiempo para formular el problema, entonces escribiré una publicación pronto.

Como ya has descubierto, esta es la razón:

 a != "" #[1] TRUE NA FALSE 

Puede hacer lo que ya descubrió, es decir, x[is.na(a) | a != ""] x[is.na(a) | a != ""] o puede setkey a setkey en a y hacer lo siguiente:

 setkey(x, a) x[!J("")] 

Respuesta de fondo de Mateo:

El comportamiento con != En NA como se destaca en esta pregunta no fue intencionado, pensando en ello. La intención original era de hecho ser diferente de [.data.frame wrt == y NA y creo que todos están contentos con eso. Por ejemplo, la pregunta frecuente 2.17 tiene:

DT[ColA==ColB] es más simple que DF[!is.na(ColA) & !is.na(ColB) & ColA==ColB,]

Esa conveniencia se logra a fuerza de:

DT[c(TRUE,NA,FALSE)] trata el NA como FALSE , pero DF[c(TRUE,NA,FALSE)] devuelve NA rows para cada NA

La motivación no es solo la conveniencia sino la velocidad, ¡ya que todos y cada uno ! , is.na , & y == son exploraciones vectoriales con asignación de memoria asociada de cada uno de sus resultados (explicado en la viñeta introductoria). Entonces, aunque x[is.na(a) | a!=""] x[is.na(a) | a!=""] es una solución que funciona, es exactamente el tipo de lógica que estaba intentando evitar en data.table. x[!a %in% ""] es un poco mejor; es decir, 2 escaneos ( %in% y ! ) en lugar de 3 ( is.na , | y != ). Pero realmente x[a != ""] Debería hacer lo que Frank esperaba (incluir NA ) en un solo escaneo.

Nueva solicitud de función presentada que enlaza con esta pregunta:

DT [col! = “”] Debería incluir NA

Gracias a Frank, Eddi y Arun. Si no he entendido correctamente, no dude en corregir, de lo contrario el cambio se realizará con el tiempo. Tendrá que hacerse de una manera que considere expresiones compuestas; por ejemplo, DT[colA=="foo" & colB!="bar"] debería excluir las filas con NA en colA pero incluir las filas donde colA no es NA pero colB es NA . De forma similar, DT[colA!=colB] debe incluir filas donde colA o colB sea NA pero no ambas. Y tal vez DT[colA==colB] debería incluir filas en las que tanto colA como colB son NA (que actualmente no lo creo, creo).