DT Y DT Tratan NA en x inconsistentemente

Esto es algo que pensé que debería hacer después de esta pregunta . Me gustaría confirmar si se trata de una falla / incoherencia antes de archivarla como tal en el rastreador R-forge.

Considera esta data.table :

 require(data.table) DT <- data.table(x=c(1,0,NA), y=1:3) 

Ahora, para acceder a todas las filas del DT que no son 0, podemos hacerlo de la siguiente manera:

 DT[x != 0] # xy # 1: 1 1 DT[!(x == 0)] # xy # 1: 1 1 # 2: NA 3 

El acceso a DT[x != 0] y DT[!(x==0)] arroja resultados diferentes cuando la operación lógica subyacente es equivalente.

Nota: Convertir esto en un data.frame y ejecutar estas operaciones dará resultados que son idénticos entre sí para ambas operaciones lógicamente equivalentes, pero ese resultado es diferente de estos dos resultados data.table. Para una explicación de por qué, mira ?`[` la sección NAs in indexing .

Editar: Dado que algunos de ustedes han insistido en igualdad con data.frame , aquí está el fragmento del resultado de las mismas operaciones en data.frame:

 DF <- as.data.frame(DT) # check ?`[` under the section `NAs in indexing` as to why this happens DF[DF$x != 0, ] # xy # 1 1 1 # NA NA NA DF[!(DF$x == 0), ] # xy # 1 1 1 # NA NA NA 

Creo que esto es una incoherencia y ambos deberían proporcionar el mismo resultado. Pero, ¿qué resultado? La documentación de [.data.table dice:

i —> Entero, vector lógico o de caracteres, expresión de nombres de columna, list o data.table.

los vectores enteros y lógicos funcionan de la misma forma que lo hacen en [.data.frame. Aparte de NA, en lógica, i se tratan como FALSO y una única NA lógica no se recicla para que coincida con el número de filas, como en [.data.frame.

Está claro por qué los resultados son diferentes de lo que se obtendría al hacer la misma operación en un data.frame . Pero aún así, dentro de data.table, si este es el caso, ambos deberían regresar:

 # xy # 1: 1 1 

[.data.table código fuente [.data.table y ahora entiendo por qué sucede esto. Vea esta publicación para una explicación detallada de por qué está sucediendo esto.

En resumen, x != 0 evalúa como “lógico” y NA se reemplaza a FALSO. Sin embargo !(x==0) , primero (x == 0) se evalúa como lógico y NA se reemplaza por FALSO. Luego ocurre la negación, lo que hace que NA convierta en TRUE .

Entonces, mi primera pregunta (o más bien la principal) es: ¿es esto un error / incoherencia? Si es así, lo archivaré como uno en data.table R-forge tracker. Si no, me gustaría saber el motivo de esta diferencia y me gustaría sugerir una corrección a la documentación que explica esta diferencia (¡a la documentación ya increíble!).

Editar: Siguiendo con los comentarios, la segunda pregunta es: ¿el manejo de data.table para subconjuntos indexando con columnas que contienen NA asemeja al de data.frame ? (Pero estoy de acuerdo, siguiendo el comentario de @Roland de que esto puede conducir a opiniones y estoy perfectamente bien si no respondo esta pregunta en absoluto).

A partir de la versión 1.8.11, el ! no desencadena un no-join para expresiones lógicas y los resultados para las dos expresiones son los mismos:

 DT <- data.table(x=c(1,0,NA), y=1:3) DT[x != 0] # xy #1: 1 1 DT[!(x == 0)] # xy #1: 1 1 

Otras dos expresiones mencionadas en la respuesta de @ mnel también se comportan ahora de manera más predecible:

 DT[!(x != 0)] # xy #1: 0 2 DT[!!(x == 0)] # xy #1: 0 2 

Creo que es un comportamiento documentado y consistente.

Lo principal a tener en cuenta es que el prefijo ! dentro del argumento i hay una bandera para no unir, entonces x != 0 y !(x==0) ya no son la misma operación lógica cuando se trabaja con el manejo documentado de NA dentro de data.table

La sección de noticias sobre el not join

 A new "!" prefix on i signals 'not-join' (aka 'not-where'), #1384i. DT[-DT["a", which=TRUE, nomatch=0]] # old not-join idiom, still works DT[!"a"] # same result, now preferred. DT[!J(6),...] # !J == not-join DT[!2:3,...] # ! on all types of i DT[colA!=6L | colB!=23L,...] # multiple vector scanning approach (slow) DT[!J(6L,23L)] # same result, faster binary search '!' has been used rather than '-' : * to match the 'not-join'/'not-where' nomenclature * with '-', DT[-0] would return DT rather than DT[0] and not be backwards compatible. With '!', DT[!0] returns DT both before (since !0 is TRUE in base R) and after this new feature. * to leave DT[+J...] and DT[-J...] available for future use 

Y de ?data.table

¡Todos los tipos de ‘i’ pueden tener el prefijo! Esto indica que no se debe unir o no se debe seleccionar. A lo largo de la documentación de data.table, donde nos referimos al tipo de ‘i’, nos referimos al tipo de ‘i’ después del ‘!’, Si está presente. Ver ejemplos.


¿Por qué es consistente con el manejo documentado de NA dentro de data.table

NA valores de NA se consideran FALSOS. Piénsalo como hacer isTRUE en cada elemento.

entonces DT[x!=0] se indexa con TRUE FALSE NA que se convierte en TRUE FALSE FALSE debido al manejo documentado de NA.

Usted quiere subconjunto cuando las cosas son VERDADERAS.

Esto significa que está obteniendo aquellos donde x! = 0 es VERDADERO (y no NA)

DT[!(x==0)] usa los estados no unirse para los que quiere todo lo que no sea 0 (que puede incluir los valores de NA ).


consultas de seguimiento / más ejemplos

DT[!(x!=0)]

 ## returns xy 1: 0 2 2: NA 3 

x!=0 es VERDADERO para un valor, por lo que el no unirse devolverá lo que no es verdadero. (es decir, lo que era FALSE (en realidad == 0 ) o NA

DT[!!(x==0)]

 ## returns xy 1: 0 2 2: NA 3 

Esto se analiza como !(!(x==0)) . El prefijo ! denota a no join, y el inner !(x==0) se analiza de forma idéntica a x!=0 , por lo que se aplica el razonamiento del caso inmediatamente anterior.

Llego un mes tarde a esta discusión, pero con los ojos frescos y leyendo todos los comentarios … sí, creo que DT[x != .] Sería mejor si incluía cualquier fila con NA en x en el resultado, y nosotros debería cambiarlo para hacer eso.

Nueva respuesta agregada a la pregunta vinculada con más antecedentes desde un ángulo diferente:

https://stackoverflow.com/a/17008872/403310

Mi opinión es que el subset hace lo correcto y tanto data.table como data.frame no lo hacen, y data.frame el más tonto de todos. Por lo que respecta a su pregunta, no, no creo que data.table deba hacer lo mismo que data.frame , debería hacer lo mismo que subset .

Para el registro, aquí está la salida del subset :

 subset(DF, x != 0) # xy #1 1 1 subset(DF, !(x == 0)) # xy #1 1 1 # # or if you want the NA's as well subset(DF, is.na(x) | x != 0) # xy #1 1 1 #3 NA 3 

Quiero elaborar un poco sobre por data.frame salida de data.frame es tonta. La primera línea en la descripción de [.data.frame dice – “Extraer o reemplazar subconjuntos de marcos de datos” . El resultado que devuelve, donde tiene una fila con nombre de fila = NA y todos los elementos iguales a NA son de ningún modo “subconjuntos” de la ttwig de datos dada, haciendo que la salida sea inconsistente con el significado de la función. También es una gran molestia desde el punto de vista del usuario, ya que uno tiene que estar siempre al tanto de estas cosas y encontrar formas de evitar este comportamiento.

En cuanto a la salida de data.table , es claramente inconsistente, pero al menos menos tonto, ya que en ambos casos devuelve subconjuntos de la tabla de datos original.