DT E DT Tratam NA em x inconsistentemente

Isso é algo que eu pensei que deveria perguntar depois dessa pergunta . Gostaria de confirmar se isso é um bug / inconsistência antes de arquivá-lo como tal no rastreador R-forge.

Considere este data.table :

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

Agora, para acessar todas as linhas do DT que não são 0, poderíamos fazer isso das seguintes maneiras:

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

Acessar DT[x != 0] e DT[!(x==0)] fornecem resultados diferentes quando a operação lógica subjacente é equivalente.

Nota: Converter isso em um data.frame e executar essas operações fornecerá resultados idênticos entre si para operações logicamente equivalentes, mas esse resultado é diferente de ambos os resultados de data.table. Para uma explicação do porquê, olhe para ?`[` sob a seção NAs in indexing .

Edit: Como alguns de vocês enfatizaram a igualdade com data.frame , aqui está o trecho da saída das mesmas operações em 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 

Eu acho que isso é uma inconsistência e ambos devem fornecer o mesmo resultado. Mas qual resultado? A documentação para [.data.table diz:

i —> Inteiro, vetor lógico ou de caractere, expressão de nomes de colunas, lista ou data.table.

vetores inteiros e lógicos funcionam da mesma maneira que em [.data.frame. Além de NAs no lógico i são tratados como FALSE e um único NA lógico não é reciclado para corresponder ao número de linhas, como é em [.data.frame.

É claro por que os resultados são diferentes do que se obteria ao fazer a mesma operação em um data.frame . Ainda assim, no data.table, se esse for o caso, ambos devem retornar:

 # xy # 1: 1 1 

Eu passei pelo código-fonte [.data.table e agora entendo por que isso está acontecendo. Veja este post para uma explicação detalhada de por que isso está acontecendo.

Resumidamente, x != 0 avaliado como “lógico” e NA é substituído por FALSO. No entanto !(x==0) , primeiro (x == 0) é avaliado como lógico e NA é substituído por FALSE. Então a negação acontece, o que resulta em NA basicamente, tornando-se TRUE .

Então, minha primeira pergunta (ou melhor) é, isso é um bug / inconsistência? Se assim for, eu vou arquivá-lo como um em rastreador de R-forge data.table. Se não, gostaria de saber o motivo dessa diferença e gostaria de sugerir uma correção na documentação que explica essa diferença (para a já impressionante documentação!).

Editar: Continuando com os comentários, a segunda pergunta é, se o manuseio do data.table para subconjuntos por indexação com colunas contendo NA assemelham ao de data.frame ?? (Mas eu concordo, seguindo o comentário do @ Roland de que isso pode ser muito bem conduzido a opiniões e eu estou perfeitamente bem em não responder a esta questão).

A partir da versão 1.8.11 o ! não aciona uma não associação para expressões lógicas e os resultados para as duas expressões são os mesmos:

 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 

Algumas outras expressões mencionadas na resposta do @mnel também se comportam de uma forma mais previsível agora:

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

Eu acho que é um comportamento documentado e consistente.

A principal coisa a notar é que o prefixo ! dentro do argumento i é um sinalizador para um não join, então x != 0 e !(x==0) não são mais a mesma operação lógica quando se trabalha com o tratamento documentado de NA dentro de data.table

A seção das notícias sobre o 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 

E de ?data.table

Todos os tipos de ‘i’ podem ser prefixados com! Isso sinaliza que uma não-junit ou não-seleção deve ser executada. Ao longo da documentação do data.table, onde nos referimos ao tipo de ‘i’, queremos dizer o tipo de ‘i’ depois do ‘!’, Se presente. Veja exemplos.


Por que é consistente com o tratamento documentado de NA dentro dos dados?

Valores de NA são considerados FALSOS. Pense nisso como fazer isTRUE em cada elemento.

então DT[x!=0] é indexado com TRUE FALSE NA que se torna TRUE FALSE FALSE devido ao manuseio de NA documentado.

Você está querendo fazer um subconjunto quando as coisas são verdadeiras.

Isso significa que você está recebendo aqueles em que x! = 0 é TRUE (e não NA)

DT[!(x==0)] usa os estados não associados que você quer tudo o que não é 0 (o que pode e includeá os valores NA ).


consultas de acompanhamento / exemplos adicionais

DT[!(x!=0)]

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

x!=0 é TRUE para um valor, então o não join retornará o que não é verdade. (ou seja, o que era FALSE (na verdade == 0 ) ou NA

DT[!!(x==0)]

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

Isso é analisado como !(!(x==0)) . O prefixo ! denota a não junit, e o interior !(x==0) é analisado de forma idêntica a x!=0 , então o raciocínio do caso imediatamente acima se aplica.

Estou um mês atrasado para esta discussão, mas com novos olhos e lendo todos os comentários … sim, eu acho que DT[x != .] Seria melhor se incluísse quaisquer linhas com NA em x no resultado, e nós deve mudá-lo para fazer isso.

Nova resposta adicionada à pergunta vinculada com plano de fundo adicional de um ângulo diferente:

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

Minha opinião é que o subset faz a coisa certa e tanto o data.table quanto o data.frame não, com data.frame fazendo o mais idiota de todos. Então, no que diz respeito à sua pergunta – não, eu não acho que o data.table deva fazer a mesma coisa que o data.frame , ele deve fazer a mesma coisa que o subset .

Para o registro, aqui está a saída do 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 

Eu quero elaborar um pouco sobre por que a saída data.frame é boba. A primeira linha na descrição de [.data.frame diz – “Extraia ou substitua subconjuntos de frameworks de dados” . A saída que ele retorna, onde tem uma linha com nome reanteiro = NA e todos os elementos iguais a NA são, em nenhum sentido, “subconjuntos” do quadro de dados dado, tornando a saída inconsistente com o significado da function. É também um incômodo enorme do ponto de vista do usuário, pois é preciso estar sempre ciente dessas coisas e encontrar maneiras de contornar esse comportamento.

No que diz respeito data.table saída do data.table – é claramente inconsistente, mas pelo menos menos tola, pois em ambos os casos ele retorna subconjuntos da tabela de dados original.