× Você precisa de ajuda para aprender R? Inscreva-se no curso de introdução ao R da Applied Epi, experimente nossos tutoriais gratuitos sobre o R, publique em nosso fórum de perguntas e respostas, ou solicite nosso suporte ao R.

50 Data Table

Este livro centra-se nos “verbos” (funções) do pacote dplyr e no operador pipe %>% do pacote magrittr como método para limpar e agrupar dados, mas o pacote data.table oferece um método alternativo que você poderá incorporar em seu uso do R.

50.1 Introdução ao Data Table

Uma tabela de dados (data table) é uma estrutura de dados bidimensional como um data frame, que permite a realização de operações complexas de agrupamento. A sintaxe data.table é estruturada para que as operações possam ser realizadas em linhas, colunas e grupos.

A estrutura é DT[i, j, by], separada por 3 partes; os argumentos i, j e by. O argumento i permite filtrar linhas, o argumento j permite operar em colunas e o argumento by permite operar em colunas por grupos.

Esta página abordará os seguintes tópicos:

  • Importação de dados e uso das funções fread() e fwrite()
  • Filtragem de linhas utilizando o argumento i
  • Utilização das funções de ajuda %like%, %chin% e %between%
  • Seleção e operação com colunas utilizando o argumento j
  • Operar por grupos utilizando o argumento by
  • Adição de dados e atualização de data tables (tabelas de dados) utilizando :=

50.2 Carregar pacotes e importar dados

Carregar pacotes

Utilizando a função p_load() de pacman, carregamos (e instalamos, se necessário) os pacotes necessários para esta análise.

pacman::p_load(
  rio,        # para importar dados
  data.table, # para agrupar e limpar dados
  tidyverse,  # permite o uso da função pipe (%>%) neste capítulo
  here 
  ) 

Importar dados

Esta página vai explorar algumas das funções centrais do pacote data.table recorrendo à mesma linelist de casos utilizada ao longo do manual.

Importamos o conjunto de dados dos casos de uma epidemia simulada de Ébola. Se você quiser baixar os dados para seguir passo a passo, veja as instruções na página Baixar livro e dados. O conjunto de dados é importado utilizando a função import() do pacote rio. Veja a página em Importar e exportar para várias formas de importação de dados. Em seguida, utilizamos data.table() para converter o quadro de dados em um data table.

linelist <- rio::import(here("data", "linelist_cleaned.xlsx")) %>% data.table()

A função fread() é utilizada para importar arquivos delimitados por caracteres, como arquivos .csv, diretamente para um formato de data table. Esta função, e sua contraparte fwrite(), utilizada para escrever data.tables como arquivos delimitados, são opções muito rápidas e computacionalmente eficientes para grandes bancos de dados.

As primeiras 20 linhas da linelist:

Comandos do R Base como dim() que são utilizados para data frames também podem ser utilizados para data tables (tabelas de dados).

dim(linelist) #dispõe o número de linhas e colunas na tabela de dados
## [1] 5888   30

50.3 O argumento i: selecionando e filtrando linhas

Relembrando a estrutura DT[i, j, by], podemos filtrar linhas usando números de linha ou expressões lógicas. O argumento i é o primeiro; portanto, a sintaxe DT[i] ou DT[i,] pode ser usada.

O primeiro exemplo recupera as primeiras 5 linhas do data table, o segundo exemplo retorna casos com 18 anos ou mais, e o terceiro exemplo gera um subconjunto de casos com 18 anos ou mais, mas não diagnosticados no Hospital Central:

linelist[1:5] # retorna da 1ª à 5ª fileira
linelist[age >= 18] # subconjunto de casos com 18 anos ou mais
linelist[age >= 18 & hospital != "Central Hospital"] # conjunto de casos com idade igual ou superior a 18 anos, mas não diagnosticados no Hospital Central

O uso de .N no argumento i representa o número total de linhas no data table. Isto pode ser usado para criar subconjuntos com base nos números das linhas:

linelist[.N] # retorna a última linha
linelist[15:.N] # retorna da 15ª à última linha

Funções de ajuda para filtragem

Data tables (tabelas de dados) utilizam funções de auxílio que facilitam a filtragem linhas. A função %like% é utilizada para corresponder a um padrão em uma coluna, %chin% é utilizada para corresponder a um caractere específico, e a função %between% é utilizada para corresponder a colunas numéricas dentro de uma faixa pré-especificada.

Nos exemplos a seguir, nós: * filtramos linhas em que a variável hospital contém “Hospital” * filtramos linhas em que o resultado é “Recover” ou “Death” * filtramos linhas em que a faixa etária (age) é 40-60 anos

linelist[hospital %like% "Hospital"] # filtrar linhas em que a variável hospital contém "Hospital"
linelist[outcome %chin% c("Recover", "Death")] # filtrar linhas em que o resultado (outcome) é "Recover" ou "Death"
linelist[age %between% c(40, 60)] # filtrar linhas em que a faixa etária (age) é de 40-60 anos

#%between% deve receber um vetor de comprimento 2, enquanto %chin% pode receber vetores de comprimento >= 1

50.4 O argumento j: seleção e cálculo nas colunas

Usando a estrutura DT[i, j, by], podemos selecionar colunas usando números ou nomes. O argumento j é o segundo; portanto, a sintaxe DT[, j] é usada. Para facilitar os cálculos no argumento j, a coluna é envolvida utilizando list() ou .().

Seleção de colunas

O primeiro exemplo recupera a primeira, terceira e quinta colunas do data table, o segundo exemplo seleciona todas as colunas, exceto as colunas gender, age, wt_kg e ht_cm. O terceiro exemplo utiliza o envelope .() para selecionar as colunas case_id e outcome.

linelist[ , c(1,3,5)]
linelist[ , -c("gender", "age", "wt_kg", "ht_cm")]
linelist[ , list(case_id, outcome)] #linelist[ , .(case_id, outcome)] funciona tão bem quanto

Cálculo nas colunas

Combinando os argumentos i e j é possível filtrar linhas e calcular colunas. Usar .N no argumento j também representa o número total de linhas no data table e pode ser útil para retornar o número de linhas após a filtragem.

Nos exemplos a seguir, nós: * contamos o número de casos que permaneceram mais de 7 dias no hospital * calculamos a idade média dos casos que vieram a óbito no hospital militar * calculamos o desvio padrão, mediana, e média da idade dos casos que se recuperaram no hospital central

linelist[days_onset_hosp > 7 , .N]
## [1] 189
linelist[hospital %like% "Military" & outcome %chin% "Death", .(mean(age, na.rm = T))] #na.rm = T remove valores N/A
##         V1
## 1: 15.9084
linelist[hospital == "Central Hospital" & outcome == "Recover", 
                 .(mean_age = mean(age, na.rm = T),
                   median_age = median(age, na.rm = T),
                   sd_age = sd(age, na.rm = T))] # esta sintaxe não utiliza as funções de ajuda, mas funciona tão bem quanto
##    mean_age median_age   sd_age
## 1: 16.85185         14 12.93857

Lembre-se que usar o envelope .() no argumento j facilita o cálculo, retorna um data table e permite a nomeação de colunas.

50.5 The by argument: computing by groups

O argumento by é o terceiro argumento na estrutura DT[i, j, by]. Ele aceita tanto um vetor de caracteres quanto a sintaxe list() ou .(). A utilização da sintaxe .() no argumento by permite renomear a coluna imediatamente.

Nos exemplos a seguir, nós: * agrupamos o número de casos por hospital * calculamos a altura média e o peso dos casos com 18 anos ou mais, de acordo com o sexo e desfecho (se eles se recuperaram ou vieram a óbito) * contamos o número de casos com tempo de internação > 7 dias, de acordo com o mês e o hospital em que foram admitidos

linelist[, .N, .(hospital)] # número de casos por hospital
##                                hospital    N
## 1:                                Other  885
## 2:                              Missing 1469
## 3: St. Mark's Maternity Hospital (SMMH)  422
## 4:                        Port Hospital 1762
## 5:                    Military Hospital  896
## 6:                     Central Hospital  454
linelist[age > 18, .(mean_wt = mean(wt_kg, na.rm = T),
                             mean_ht = mean(ht_cm, na.rm = T)), .(gender, outcome)] #NAs representam as categorias em que os dados estão faltando
##    gender outcome  mean_wt  mean_ht
## 1:      m Recover 71.90227 178.1977
## 2:      f   Death 63.27273 159.9448
## 3:      m   Death 71.61770 175.4726
## 4:      f    <NA> 64.49375 162.7875
## 5:      m    <NA> 72.65505 176.9686
## 6:      f Recover 62.86498 159.2996
## 7:   <NA> Recover 67.21429 175.2143
## 8:   <NA>   Death 69.16667 170.7917
## 9:   <NA>    <NA> 70.25000 175.5000
linelist[days_onset_hosp > 7, .N, .(month = month(date_hospitalisation), hospital)]
##     month                             hospital  N
##  1:     5                    Military Hospital  3
##  2:     6                        Port Hospital  4
##  3:     7                        Port Hospital  8
##  4:     8 St. Mark's Maternity Hospital (SMMH)  5
##  5:     8                    Military Hospital  9
##  6:     8                                Other 10
##  7:     8                        Port Hospital 10
##  8:     9                        Port Hospital 28
##  9:     9                              Missing 27
## 10:     9                     Central Hospital 10
## 11:     9 St. Mark's Maternity Hospital (SMMH)  6
## 12:    10                              Missing  2
## 13:    10                    Military Hospital  3
## 14:     3                        Port Hospital  1
## 15:     4                    Military Hospital  1
## 16:     5                                Other  2
## 17:     5                     Central Hospital  1
## 18:     5                              Missing  1
## 19:     6                              Missing  7
## 20:     6 St. Mark's Maternity Hospital (SMMH)  2
## 21:     6                    Military Hospital  1
## 22:     7                    Military Hospital  3
## 23:     7                                Other  1
## 24:     7                              Missing  2
## 25:     7 St. Mark's Maternity Hospital (SMMH)  1
## 26:     8                     Central Hospital  2
## 27:     8                              Missing  6
## 28:     9                                Other  9
## 29:     9                    Military Hospital 11
## 30:    10                        Port Hospital  3
## 31:    10                                Other  4
## 32:    10 St. Mark's Maternity Hospital (SMMH)  1
## 33:    10                     Central Hospital  1
## 34:    11                              Missing  2
## 35:    11                        Port Hospital  1
## 36:    12                        Port Hospital  1
##     month                             hospital  N

Data.table também permite expressões em serquência:

linelist[, .N, .(hospital)][order(-N)][1:3] #1º seleciona todos os casos por hospital, 2º ordena os casos em ordem decrescente, 3º seleciona um subconjunto dos 3 hospitais com o maior número de casos
##             hospital    N
## 1:     Port Hospital 1762
## 2:           Missing 1469
## 3: Military Hospital  896

Nestes exemplos, estamos seguindo a suposição de que uma linha no data table é igual a um novo caso, e assim podemos usar o .N para representar o número de linhas no data table. Outra função útil para representar o número de casos únicos é uniqueN(), que retorna o número de valores únicos em uma determinada entrada. Como ilustrado aqui:

linelist[, .(uniqueN(gender))] # lembre que o envelope .() no argumento j retorna um data table
##    V1
## 1:  3

A resposta é 3, pois os valores únicos na coluna de gênero são m, f e N/A. Compare com a função unique() do R Base, que retorna todos os valores únicos em uma determinada entrada:

linelist[, .(unique(gender))]
##      V1
## 1:    m
## 2:    f
## 3: <NA>

Para encontrar o número de casos únicos em um determinado mês, escrevemos o seguinte:

linelist[, .(uniqueN(case_id)), .(month = month(date_hospitalisation))]
##     month   V1
##  1:     5   62
##  2:     6  100
##  3:     7  198
##  4:     8  509
##  5:     9 1170
##  6:    10 1228
##  7:    11  813
##  8:    12  576
##  9:     1  434
## 10:     2  310
## 11:     3  290
## 12:     4  198

50.6 Adicionar e atualizar data tables (tabelas de dados)

O operador := é utilizado para adicionar ou atualizar dados em um data table. A adição de colunas pode ser feita das seguintes maneiras:

linelist[, adult := age >= 18] # adiciona uma coluna
linelist[, c("child", "wt_lbs") := .(age < 18, wt_kg*2.204)] #para adicionar múltiplas colunas é necessário usar as sintaxes c(""), list() ou .() syntax
linelist[, `:=` (bmi_in_range = (bmi > 16 & bmi < 40),
                         no_infector_source_data = is.na(infector) | is.na(source))] #Este método utiliza `:=` como um operador funcional 
linelist[, adult := NULL] # deleta a coluna

Outras agregações complexas estão além do objetivo deste capítulo introdutório, mas a ideia é fornecer uma alternativa popular e viável ao dplyr para agrupamento e limpeza de dados. O pacote data.table é um grande pacote que permite um código limpo e legível.