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
) 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()efwrite() - 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.
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 CentralO 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 linhaFunçõ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 >= 150.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 quantoCá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
<num>
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
<num> <num> <num>
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
<char> <int>
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
<char> <char> <num> <num>
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
<num> <char> <int>
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
<char> <int>
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
<int>
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
<char>
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
<num> <int>
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 colunaOutras 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.
50.7 Recursos
Aqui estão alguns recursos úteis para maiores informações: * https://cran.r-project.org/web/packages/data.table/vignettes/datatable-intro.html * https://github.com/Rdatatable/data.table * https://s3.amazonaws.com/assets.datacamp.com/img/blog/data+table+cheat+sheet.pdf * https://www.machinelearningplus.com/data-manipulation/datatable-in-r-complete-guide/ * https://www.datacamp.com/community/tutorials/data-table-r-tutorial
Você pode realizar qualquer função de resumo sobre dados agrupados; veja a Cheat Sheet para mais informações: https://s3.amazonaws.com/assets.datacamp.com/blog_assets/datatable_Cheat_Sheet_R.pdf
