18  ✅ Valores faltantes

18.1 Introdução

Você já aprendeu o básico sobre valores faltantes (missing values) no início deste livro. Você os viu pela primeira vez no Capítulo 1, onde resultaram em um aviso ao criar um gráfico, bem como na Seção 3.5.2, onde interferiram no cálculo de estatísticas sumarizadas, e aprendeu sobre sua natureza de propagação e como verificar sua presença na Seção 12.2.2. Agora voltaremos a eles com mais profundidade, para que você possa aprender mais detalhes.

Começaremos discutindo algumas ferramentas gerais para trabalhar com valores faltantes registrados como NA (do inglês Not Available - “não disponível”). Em seguida, exploraremos a ideia de valores faltantes implícitos, ou seja, valores que estão simplesmente ausentes em seus dados, e mostraremos algumas ferramentas que você pode usar para torná-los explícitos. Terminaremos com uma discussão relacionada sobre grupos vazios, causados ​​por níveis de fatores que não aparecem nos dados.

18.1.1 Pré-requisitos

As funções para trabalhar com dados ausentes vêm principalmente dos pacotes dplyr e tidyr, que são membros principais do tidyverse.

18.2 Valores faltantes explícitos

Para começar, vamos explorar algumas ferramentas úteis para criar ou eliminar valores explícitos ausentes, ou seja, células onde você vê um NA.

18.2.1 Última observação levada adiante

Um uso comum de valores faltantes (missing values) é como conveniência na entrada de dados. Quando os dados são inseridos manualmente, os valores ausentes às vezes indicam que o valor da linha anterior foi repetido (levado adiante):

tratamento <- tribble(
  ~pessoa,           ~tratamento, ~resposta,
  "Derrick Whitmore", 1,         7,
  NA,                 2,         10,
  NA,                 3,         NA,
  "Katherine Burke",  1,         4
)

Você pode preencher esses valores ausentes com a função tidyr::fill(). Funciona como a função select(), pegando um conjunto de colunas como argumento:

tratamento |>
  fill(everything())
#> # A tibble: 4 × 3
#>   pessoa           tratamento resposta
#>   <chr>                 <dbl>    <dbl>
#> 1 Derrick Whitmore          1        7
#> 2 Derrick Whitmore          2       10
#> 3 Derrick Whitmore          3       10
#> 4 Katherine Burke           1        4

Este tratamento às vezes é chamado de “última observação realizada” (last observation carried forward) ou locf, abreviadamente. Você pode usar o argumento .direction para preencher valores ausentes que foram gerados de maneiras mais exóticas.

18.2.2 Valores fixos

Algumas vezes, os valores ausentes representam algum valor fixo e conhecido, mais comumente 0. Você pode usar a dplyr::coalesce() para substituí-los:

x <- c(1, 4, 5, 7, NA)
coalesce(x, 0)
#> [1] 1 4 5 7 0

Às vezes você encontrará o problema oposto, onde algum valor concreto na verdade representa um valor ausente. Isso normalmente surge em dados gerados por softwares mais antigos que não possuem uma maneira adequada de representar valores ausentes; portanto, deve-se usar algum valor especial como 99 ou -999.

Se possível, lide com isso ao ler os dados, por exemplo, usando o argumento na para readr::read_csv(), por exemplo, read_csv(caminho, na = "99"). Se você descobrir o problema mais tarde, ou se sua fonte de dados não fornecer uma maneira de lidar com isso na leitura, você pode usar dplyr::na_if():

x <- c(1, 4, 5, 7, -99)
na_if(x, -99)
#> [1]  1  4  5  7 NA

18.2.3 NaN

Antes de continuarmos, há um tipo especial de valor ausente que você encontrará de tempos em tempos: um NaN (pronuncia-se “nan”) ou em inglês, not a number (em português, seria como “não é um número”). Não é tão importante saber sobre ele, já que geralmente se comporta como `NA:

x <- c(NA, NaN)
x * 10
#> [1]  NA NaN
x == 1
#> [1] NA NA
is.na(x)
#> [1] TRUE TRUE

Se você tiver um caso raro e precisar distinguir um NA de um NaN, você pode usar is.nan(x).

Você geralmente encontrará um NaN ao realizar uma operação matemática que tem um resultado indeterminado:

0 / 0 
#> [1] NaN
0 * Inf
#> [1] NaN
Inf - Inf
#> [1] NaN
sqrt(-1)
#> Warning in sqrt(-1): NaNs produced
#> [1] NaN

18.3 Valores faltantes implícitos

Até agora falamos sobre valores que estão explicitamente ausentes, ou seja, você pode ver um NA em seus dados. Mas os valores também podem estar implicitamente ausentes, se uma linha inteira de dados estiver simplesmente ausente dos dados. Vamos ilustrar a diferença com um conjunto de dados simples que registra o preço de algumas ações a cada trimestre:

acoes <- tibble(
  ano  = c(2020, 2020, 2020, 2020, 2021, 2021, 2021),
  trimestre   = c(   1,    2,    3,    4,    2,    3,    4),
  preco = c(1.88, 0.59, 0.35,   NA, 0.92, 0.17, 2.66)
)

Este conjunto de dados tem duas observações faltantes:

  • O preco no quarto trimestre de 2020 está explicitamente ausente, porque o seu valor é NA.

  • O preço para o primeiro trimestre de 2021 está implicitamente ausente, porque simplesmente não aparece no conjunto de dados.

Uma maneira de pensar sobre a diferença é como este diálogo meio filosófico:

Um valor ausente explícito é a presença de uma ausência.

Um valor ausente implícito é a ausência de presença.

Às vezes, você deseja tornar explícitos os valores ausentes implícitos para ter algo físico com o qual trabalhar. Em outros casos, valores faltantes explícitos são impostos pela estrutura dos dados e você deseja se livrar deles. As seções a seguir discutem algumas ferramentas para alternar entre ausências implícitas e explícitas.

18.3.1 Pivotagem

Você já viu uma ferramenta que pode tornar explícitas as ausências implícitas e vice-versa: pivotagem. Deixar os dados mais largos (com mais colunas) pode tornar explícitos os valores ausentes implícitos porque cada combinação de linhas e novas colunas deve ter algum valor. Por exemplo, se pivotarmos acoes para colocar o trimestre nas colunas, ambos os valores faltantes se tornarão explícitos:

acoes |>
  pivot_wider(
    names_from = trimestre, 
    values_from = preco
  )
#> # A tibble: 2 × 5
#>     ano   `1`   `2`   `3`   `4`
#>   <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1  2020  1.88  0.59  0.35 NA   
#> 2  2021 NA     0.92  0.17  2.66

Por padrão, tornar os dados mais longos preserva os valores ausentes explícitos, mas se eles forem valores estruturalmente ausentes que só existem porque os dados não estão organizados, você pode eliminá-los (torná-los implícitos) definindo values_drop_na = TRUE. Veja os exemplos na Seção 5.2 para mais detalhes.

18.3.2 Completar

A função tidyr::complete() permite gerar valores faltantes explícitos, fornecendo um conjunto de variáveis ​​que definem a combinação de linhas que devem existir. Por exemplo, sabemos que todas as combinações de ano e trimestre devem existir nos dados de acoes:

acoes |>
  complete(ano, trimestre)
#> # A tibble: 8 × 3
#>     ano trimestre preco
#>   <dbl>     <dbl> <dbl>
#> 1  2020         1  1.88
#> 2  2020         2  0.59
#> 3  2020         3  0.35
#> 4  2020         4 NA   
#> 5  2021         1 NA   
#> 6  2021         2  0.92
#> # ℹ 2 more rows

Normalmente, você chamará complete() com nomes de variáveis ​​existentes, preenchendo as combinações que faltam. No entanto, às vezes as variáveis ​​individuais estão incompletas, então você pode fornecer seus próprios dados. Por exemplo, você pode saber que o conjunto de dados acoes deve ser executado de 2019 a 2021, então você pode fornecer explicitamente esses valores para ano:

acoes |>
  complete(ano = 2019:2021, trimestre)
#> # A tibble: 12 × 3
#>     ano trimestre preco
#>   <dbl>     <dbl> <dbl>
#> 1  2019         1 NA   
#> 2  2019         2 NA   
#> 3  2019         3 NA   
#> 4  2019         4 NA   
#> 5  2020         1  1.88
#> 6  2020         2  0.59
#> # ℹ 6 more rows

Se o intervalo de uma variável estiver correto, mas nem todos os valores estiverem presentes, você pode usar a full_seq(x, 1) para gerar todos os valores de min(x) a max(x) espaçados por 1.

Em alguns casos, o conjunto completo de observações não pode ser gerado por uma simples combinação de variáveis. Nesse caso, você pode fazer manualmente o que complete() faz por você: criar um dataframe que contenha todas as linhas que deveriam existir (usando qualquer combinação de técnicas necessárias) e, em seguida, combiná-lo com seu conjunto de dados original com dplyr::full_join().

18.3.3 Uniões (joins)

Isso nos leva a outra forma importante de revelar observações implicitamente ausentes: uniões (joins). Você aprenderá mais sobre uniões na Capítulo 19, mas gostaríamos de mencioná-las rapidamente aqui, já que muitas vezes você só pode saber que valores estão faltando em um conjunto de dados quando você o compara com outro.

dplyr::anti_join(x, y) é uma função particularmente útil aqui porque seleciona apenas as linhas em x que não têm correspondência em y. Por exemplo, podemos usar dois anti_join() para revelar que faltam informações de quatro aeroportos e 722 aviões mencionados em voos:

library(dados)

voos |> 
  distinct(codigo_aeroporto = destino) |> 
  anti_join(aeroportos)
#> Joining with `by = join_by(codigo_aeroporto)`
#> # A tibble: 4 × 1
#>   codigo_aeroporto
#>   <chr>           
#> 1 BQN             
#> 2 SJU             
#> 3 STT             
#> 4 PSE

voos |> 
  distinct(codigo_cauda) |> 
  anti_join(avioes)
#> Joining with `by = join_by(codigo_cauda)`
#> # A tibble: 722 × 1
#>   codigo_cauda
#>   <chr>       
#> 1 N3ALAA      
#> 2 N3DUAA      
#> 3 N542MQ      
#> 4 N730MQ      
#> 5 N9EAMQ      
#> 6 N532UA      
#> # ℹ 716 more rows

18.3.4 Exercícios

  1. Você consegue encontrar alguma relação entre a companhia_aerea e as linhas que parecem estar faltando em avioes?

18.4 Fatores e grupos vazios

Um último tipo de ausência é a de grupo vazio, um grupo que não contém nenhuma observação, que pode surgir ao trabalhar com fatores (factors). Por exemplo, imagine que temos um conjunto de dados que contém algumas informações de saúde sobre pessoas:

saude <- tibble(
  nome   = c("Ikaia", "Oletta", "Leriah", "Dashay", "Tresaun"),
  fumante = factor(c("nao", "nao", "nao", "nao", "nao"), levels = c("sim", "nao")),
  idade    = c(34, 88, 75, 47, 56),
)

E queremos contar o número de fumantes com a função dplyr::count():

saude |> count(fumante)
#> # A tibble: 1 × 2
#>   fumante     n
#>   <fct>   <int>
#> 1 nao         5

Este conjunto de dados contém apenas não fumantes, mas sabemos que existem fumantes; o grupo de não fumantes está vazio. Podemos solicitar count() para manter todos os grupos, mesmo aqueles não vistos nos dados usando .drop = FALSE:

saude |> count(fumante, .drop = FALSE)
#> # A tibble: 2 × 2
#>   fumante     n
#>   <fct>   <int>
#> 1 sim         0
#> 2 nao         5

O mesmo princípio se aplica aos eixos discretos (discrete axis) do ggplot2, que também eliminarão níveis que não possuem nenhum valor. Você pode forçá-los a serem exibidos fornecendo drop = FALSE ao eixo discreto apropriado:

ggplot(saude, aes(x = fumante)) +
  geom_bar() +
  scale_x_discrete()

ggplot(saude, aes(x = fumante)) +
  geom_bar() +
  scale_x_discrete(drop = FALSE)

Um gráfico de barras com um único valor no eixo x, “não”.

O mesmo gráfico de barras do último gráfico, mas agora com dois valores no eixo-x, "sim" e "não". Não há barra para a categoria “sim”.

O mesmo problema surge de forma mais geral com dplyr::group_by(). E novamente você pode usar .drop = FALSE para preservar todos os níveis dos fatores:

saude |> 
  group_by(fumante, .drop = FALSE) |> 
  summarize(
    n = n(),
    idade_media = mean(idade),
    idade_min = min(idade),
    idade_max = max(idade),
    idade_desvpad = sd(idade)
  )
#> # A tibble: 2 × 6
#>   fumante     n idade_media idade_min idade_max idade_desvpad
#>   <fct>   <int>       <dbl>     <dbl>     <dbl>         <dbl>
#> 1 sim         0         NaN       Inf      -Inf          NA  
#> 2 nao         5          60        34        88          21.6

Obtemos alguns resultados interessantes aqui porque ao sumarizar um grupo vazio, as funções de sumarização são aplicadas a vetores de comprimento zero. Há uma distinção importante entre vetores vazios, que têm comprimento 0, e valores ausentes, cada um dos quais tem comprimento 1.

# Um vetor com dois valores ausentes
x1 <- c(NA, NA)
length(x1)
#> [1] 2

# Um vetor contendo nada
x2 <- numeric()
length(x2)
#> [1] 0

Todas as funções de sumarização funcionam com vetores de comprimento zero, mas podem retornar resultados surpreendentes à primeira vista. Aqui vemos mean(idade) retornando NaN porque mean(idade) = sum(age)/length(idade) que aqui é 0/0. max() e min() retornam -Inf e Inf para vetores vazios, então se você combinar os resultados com um vetor não vazio de novos dados e recalcular, você obterá o mínimo ou máximo dos novos dados1.

Às vezes, uma abordagem mais simples é realizar a sumarização e então tornar explícitas as faltas implícitas com complete().

saude |> 
  group_by(fumante) |> 
  summarize(
    n = n(),
    idade_media = mean(idade),
    idade_min = min(idade),
    idade_max = max(idade),
    idade_desvpad = sd(idade)
  ) |> 
  complete(fumante)
#> # A tibble: 2 × 6
#>   fumante     n idade_media idade_min idade_max idade_desvpad
#>   <fct>   <int>       <dbl>     <dbl>     <dbl>         <dbl>
#> 1 sim        NA          NA        NA        NA          NA  
#> 2 nao         5          60        34        88          21.6

A principal desvantagem desta abordagem é que você obtém um NA para a contagem, mesmo sabendo que deveria ser zero.

18.5 Resumo

Valores faltantes são estranhos! Às vezes eles são registrados como um NA explícito, mas outras vezes você só os percebe pela sua ausência. Este capítulo forneceu algumas ferramentas para trabalhar com valores faltantes explícitos, ferramentas para descobrir valores faltantes implícitos e discutiu algumas das maneiras pelas quais os implícitos podem se tornar explícitos e vice-versa.

No próximo capítulo, abordamos o capítulo final desta parte do livro: uniões. Isso é uma pequena mudança em relação aos capítulos até agora porque discutiremos ferramentas que funcionam com data frames como um todo, não algo que você coloca dentro de um data frame.


  1. Em outras palavras, min(c(x, y)) é sempre igual a min(min(x), min(y)).↩︎