Valores missing — Parte 2

Como lidar com missings?

Gabriel Rodrigues
Datapsico
8 min readAug 28, 2020

--

Semana passada vimos por que missings existem e as formas mais comuns de ocorrerem. Fazendo uma retomada rápida: missings são valores nulos/vazios em um dataset.

Vamos relembrar. Existem 3 tipos de missing:

  1. Missings completamente aleatórios (MCAR).
    Os completamente aleatórios ocorreram por erro técnico ou obra do acaso. Algo que se perdeu no caminho e do qual não fazemos ideia do porquê.
  2. Missings aleatórios (MAR).
    Os aleatórios existem porque a existência de uma outra variável aumenta a probabilidade dessa resposta não existir.
  3. Missings não aleatórios (MNAR).
    Os não aleatórios não são aleatórios — existem porque a variável em si é a causa de não existir um escore dela mesma.

Imagine que você possui um banco de dados com alguns missing. Você precisa realizar algumas análises mais robustas do que análises descritivas, e por isso, necessita lidar com esses missings.

É necessário lidar com eles para (1) prevenir vieses que os missings podem trazer e (2) possibilitar mais poder à análise que você quer fazer.

Você tem três grandes opções quando se trata de lidar com missings:

  1. Excluir observações que possuam qualquer tipo de missing: esse método é chamado de listwise deletion, e envolve a exclusão de toda linha que tenha pelo menos um valor missing.
  2. Excluir observações que possuam missing na variável que você quer utilizar para a análise: esse método é chamado de pairwise deletion, e baseia-se na exclusão daquela linha durante ou para a execução de uma análise. Assim, essa linha não é excluída do dataset necessariamente.
  3. Substituir o valor missing por outros valores: isso envolve a substituição do valor missing de uma célula por um valor relevante para aquela observação, como a média, mediana, imputação por regressão etc.

Vamos ver essas três grandes opções as dividindo em duas grandes partes:

(1) deletando missings e

(2) imputando novos valores.

Deletando ou imputando?

Para entender a lógica de deleção ou imputação, é importante sempre ter em mente a consideração acerca do tipo de valor missing que estamos lidando.

Isso porque cada tipo de missing nos traz uma informação relevante acerca do possível valor daquele dado. Sendo assim, em alguns casos, será melhor substituir o valor missing por outro valor. Em outros casos, o melhor será a realização de imputação dos dados.

Evita-se a remoção por completo de casos que tenham um missing (listwise deletion)— isso porque missings são comuns. Entretanto, linhas que contenham missings relevantes podem ser excluídas sem trazer muito prejuízo às análises.

De forma geral:

  1. Missings completamente aleatórios (MCAR).
    Podemos tanto deletar da análise (pairwise deletion) como realizar a imputação de valores de tendência central em missings completamente aleatórios. Por não terem muita relação com outras colunas, o valor de tendência central é mais recomendado para a imputação desses missings.
  2. Missings aleatórios (MAR).
    Podemos tanto deletar da análise (pairwise deletion) como realizar imputação de valores relevantes (por meio de regressão e outros métodos) em missings aleatórios
  3. Missings não aleatórios (MNAR).
    Podemos tanto deletar da análise (pairwise deletion) como realizar imputação de valores de tendência central, já que missings não aleatórios não possuiriam relação com outras variáveis do banco de dados. O preferível aqui seria a remoção de casos missing da análise. Isso porque missings não aleatórios não recebem seu valor por um motivo. Se esse dado não consegue ser realmente acessado, não temos como ter certeza se nossa imputação está enviesando ou não a análise. Casos de MNAR precisam ser analisados individualmente e não serão tratados aqui nesse post (esse post em inglês fala um pouco sobre MNAR).

Feitas essas considerações, vamos utilizar esse banco de dados aqui para entender melhor quais os possíveis tratamentos que podemos utilizar com nosso banco de dados (você não precisa baixar o banco se não quiser).

Os exemplos de tratamento de missings serão feitos com o R. Todo o código pode ser copiado e colado daqui para você aprender junto. Ah, vamos utilizar o pacote tidyr (que já foi visto no PsicoData nesse post) para a maioria dos tratamentos, e algumas linhas de código para gerar uma função que vai realizar a imputação com regressão.

Vamos começar?

Deletando missings

Em primeiro lugar, vamos carregar nosso banco de dados.

Esse é um banco sobre bibliotecas públicas de diversos países, junto com o dinheiro (em dólar) de quanto cada país gastou com suas bibliotecas, a quantidade de pessoas que trabalhavam nas bibliotecas, a quantidade de usuários e de livros dessas bibliotecas.

O pacote Amelia tem essa ótima função chamada missmap() que nos permite visualizar os missings de uma maneira super legal. Aprendi essa com a Natália Zaniboni. Ah, caso não saiba, quando escrevemos “‘nome do pacote’ + ‘:’ + ‘função’”, estamos puxando essa função diretamente do pacote, sem precisar carregar todas as outras funções que o pacote oferece — o que economiza espaço na memória.

Olha o resultado que temos só com três linhas de código:

Show! Entendemos então que nossas três variáveis com mais missings são:

  • gastos: o quanto um país gastou com bibliotecas públicas,
  • total_trabalham: o número total de pessoas que eram bibliotecárias,
  • total_usuarios: o número total de pessoas que usavam as bibliotecas.

Excluindo linhas com tidyr::drop_na()

Olhando nosso banco de dados, vemos que ele está assim:

Vemos que temos 219 entradas e um total de 8 colunas.

Logo no início podemos fazer algo bem radical e excluir todas as linhas que contenham qualquer tipo de missing (listwise deletion). Fazemos isso usando a função drop_na() do pacote tidyr.

Agora não temos mais nenhum missing em nenhum lugar. Entretanto, temos 79 casos a menos.

Digamos que ao invés de excluirmos todas as linhas que tivesse ao menos um missing, quiséssemos excluir qualquer linha que tenha missing na coluna total_usuarios. Para isso, bastaria mudar o código só um pouquinho para tidyr::drop_na(data, total_usuarios)

Filtrando apenas pela coluna total_usuarios, o nosso banco fica com 180 casos.

Substituindo missings

Como já falamos, a substituição muitas vezes tende a ser uma solução mais adequada para lidar com os valores.

Substituição de cima para baixo e de baixo para cima: tidyr::fill()

Uma substituição possível de ser usada é a com a função fill() do pacote tidyr. A solução provida por essa função é a de pegar os valores que não estão missings mais próximos para preencher os valores missings das variáveis. Não é uma solução muito adequada, então recomendo você entender ela melhor por aqui.

Substituição pela média ou outros valores com tidyr::replace_na()

Essa pode ser uma boa solução, principalmente quando temos uma boa ideia da medida central das nossas variáveis. Digamos que queremos substituir os missings de gastos, total_trabalham e total_usuarios pelas respectivas médias de seus grupos.

Para usar replace_na() basta nós colocarmos a lista das nossas variáveis junto com a função OU o valor que queremos para essas variáveis.

Perceba que aqui é um pouco mais complicado. Isso porque precisamos:

  • Definir nossa variável depois de abrir uma lista.
  • Adicionar ‘=’ e começar a especificar qual valor os missings dessa coluna receberão.
  • Já que as duas últimas colunas da função contam o número de pessoas, adicionei na frente a função floor() , que arredonda esse valor para baixo.

Olhemos para o resultado agora. Basta olhar para total_trabalham para vermos que a média talvez não seja a melhor representação de tendência central dessa variável. O valor novo para as linhas que continham missing (1, 4 e 6) passou a ser 5002, o que não parece ser a realidade desses países.

Em gastos, o mesmo é percebido. Por exemplo, na linha 4, o país American Samoa tinha um missing em gastos que foi substituído pela média dessa coluna. O novo valor em dólares de gastos com as 32 bibliotecas desse país passou a ser de R$357.736.161. Um valor absurdamente maior do que o provável valor real.

Vimos o problema de se substituir pela média. Ela é facilmente influenciada por valores extremos.

Uma solução poderia ser substituir pela mediana. A função seria basicamente a mesma e o resultado bem diferente:

O novo valor para os missings de total_trabalham agora é 175. O valor novo para as linhas que continham missing em gastos passou a ser R$1.810.863, o que parece estar mais adequado à realidade dos países que estamos vendo aqui. Já os missings de total_usuarios receberam o valor de 316.202, também mais condizente com os dados que estamos explorando.

Uma simples mudança da média para a mediana parece ter feito uma grande diferença aqui.

Imputação por regressão

Podemos personalizar ainda mais, caso a caso, o valor dessas três variáveis que contém missing.

Podemos fazer isso utilizando-se da regressão linear, um método que busca estimar os valores de y a partir de variáveis x.

Aqui, vamos estimar o valor de gastos, total_trabalham e total_usuarios usando as outras variáveis numéricas do nosso banco. Quais são essas?

Em todos os casos, possuímos dois valores numéricos que não se alteram: total_biblios e total_volumes.

Fazendo uma rápida regressão linear (tópico para um próximo post no PsicoData) para cada coluna temos que em:

  • gastos: conseguimos 37,83% de ajuste do nosso modelo,
  • total_trabalham: conseguimos 65,01% de ajuste do nosso modelo,
  • total_usuarios: conseguimos 16,45% de ajuste do nosso modelo.

Para fazer uma imputação simples com regressão, não vamos utilizar nenhum pacote, vamos criar a nossa própria função (o que aprendi a fazer com esse ótimo capítulo sobre missings).

  • Criamos o modelo para cada coluna com a função lm()
    Aqui, a primeira variável é a ser predita, e as outras são os valores que vamos utilizar para predizer a variável. No primeiro caso, quando escrevemos gastos ~ total_biblios + total_volumes — 1 estamos querendo dizer que os valores de gastos serão preditos pelos valores das colunas total_biblios e total_volumes. O -1 significa que nós estamos retirando desse cálculo o valor do intercepto, que nesses casos eram bem significativos.
  • Predizemos com esse modelo os valores de cada linha usando a função predict()
  • Criamos a função imputar()w
    Essa função vai receber uma coluna, depois vai verificar se o valor em que ela está dessa coluna é um missing. A partir disso, se o valor for missing, o valor a ser imputado será o da fórmula que passamos para ela, se não o valor continuará igual.

Agora vamos usar tudo isso que fizemos para imputar esses valores no nosso banco de dados!

Agora os valores parecem bem mais adequados do que antes! Note que cada um dos valores são diferentes — isso porque estão baseados no valor único que cada país possui sobre o total de bibliotecas e o total de volumes dessas bibliotecas.

Esse tipo de imputação é chamada de imputação determinística. Isso porque estamos informando, através da fórmula, qual o valor que essa variável deve seguir. Outro tipo de imputação é a imputação aleatória, que não iremos abordar aqui por ser um assunto que eu, Gabriel, ainda não domino.

Finalizando, vamos novamente usar a função Amelia::missmap(data) para ver como estão os missings no nosso banco de dados. O resultado é esse aqui:

Como esperado, 0 missings!

Existem diversas formas de se lidar com missings. Essas são apenas as formas mais simples para se começar a manipular esses dados faltantes. Sempre que formos realizar exclusões ou imputações, é importante entender sobre qual o tipo de missing que estamos manipulando.

Vale citar também dois pacotes úteis para imputação de valores missing, caso lhe interesse. Esses pacotes são o pacote VIM e também o pacote mice.

Contato

Espero que tenha gostado! Qualquer dúvida, observação ou comentário são muito bem-vindos! Fique à vontade para se manifestar e vamos aprender juntos 😄

Para falar comigo, é só entrar em qualquer um desses links.

--

--

Gabriel Rodrigues
Datapsico

Sou um psicólogo que trabalha com Análise e Ciência de Dados desde a graduação — busco criar e compartilhar conteúdo sobre esses assuntos. linktr.ee/gabrielrr