Neste notebook, iremos demonstrar como acessar os dados do Sistema de Informação sobre Mortalidade - Declaração de Óbitos Fetais (SIM-DOFET) indexados pela Plataforma de Ciência de Dados aplicada à Saúde (PCDaS) através do R.
Os dados do SIM-DOFET estão disponíveis em um índice do ElasticSearch (ES), que contém todos os registros de óbito fetal atualizados anualmente.
Primeiro definimos uma função auxiliar para carregar os pacotes necessários à execução deste tutorial e instalar algum pacote caso este não esteja disponível.
loadlibrary <- function(x){
if (!require(x,character.only = TRUE)) {
install.packages(x, dep=TRUE)
if(!require(x,character.only = TRUE)) stop("Package not found")
}
}
O acesso ao índice no ES é realizado através do pacote elastic
.
loadlibrary("elastic")
Vamos também utilizar outras bibliotecas do R para facilitar a manipulação dos dados obtidos e um último comando para ajustar os tamanhos dos gráficos gerados.
packages <- c("dplyr", "curl", "jsonlite", "ggplot2", "getPass")
lapply(packages, loadlibrary)
options(repr.plot.width=15, repr.plot.height=10)
O primeiro passo é informar ao R os parâmetros de conexão com o índice no ES.
Nos parâmetros es_user
e es_pwd
, informe o mesmo usuário e senha que você usar para acessar a plataforma.
Observação: As funções "readline()" e "getPass()" abrem uma caixa de texto para digitar seu login e senha
es_host <- "dados-pcdas.icict.fiocruz.br"
es_port <- 443
es_transport_schema <- "https"
es_user <- readline("Digite seu usuário da PCDaS: ")
es_pwd <- getPass("Digite sua senha da PCDaS: ")
Em seguida, criamos um objeto (es_dofet
) para acesso ao índice do ES contendo os dados do SIM-DOFET.
es_dofet <- elastic::connect(host = es_host,
port = es_port,
transport_schema = es_transport_schema,
user = es_user,
pwd = es_pwd)
Podemos testar a conexão pedindo algumas informações básicas sobre o Elasticsearch.
print(es_dofet)
Podemos executar buscas nos dados e ver os documentos (registros) do índice (banco do SIM-DOFET) com os comandos count
e Search
.
Para fins de deixar as buscas mais intuitivas agora no início desse tutorial serão criadas 2 funções, uma para pesquisas agregadas e outra para buscas generalizadas. Ambas as funções teram como retorno um objeto no formato data.frame
.
Como exemplo do comando count
pegaremos a contagem total de registros no índice do SIM-DOFET. Em seguida faremos queries com as funções criadas.
elastic::count(es_dofet, "datasus-sim-dofet")
getColnames <- function(colunas){
colunas <- lapply(X = colunas, FUN = function(t) gsub(pattern = "_source.", replacement = "", x = t, fixed = TRUE))
return(colunas)
}
convertSearchToDF <- function(size=100, index, connection, elastic_query){
search_result <- elastic::Search(conn=connection, index=index, body=elastic_query, asdf=TRUE,size=size)
df <- search_result$hits$hits
df[c("_index", "_type", "_id", "_score")] <- NULL
rownames(df) <- NULL
colnames(df) <- getColnames(colnames(df))
return(df)
}
convertAggsToDF <- function(index, connection, size=100, elastic_query){
search_result <- elastic::Search(conn=connection, index=index, size=size, body=elastic_query, asdf = TRUE)$aggregations
outputList <- list()
outputList <- c(outputList, search_result)
records <- lapply(outputList, "[[", "buckets")
df <- records$a1
return(df)
}
Logo abaixo criaremos a query tudo
para pegarmos todos os registros completos do índice, de acordo com o parâmetro size
pré definido na função ele retornará um total de 100 registros podendo ser aumentado até 10.000. Vamos ver mais a frente que dificilmente iremos precisar mais do que isso, pois iremos trabalhar com agregações.
tudo <- '{
"query": {
"match_all": {}
}
}'
df <- convertSearchToDF(size=100, index="datasus-sim-dofet", connection=es_dofet, elastic_query = tudo)
head(df)
Se quisermos gerar tabelas mais complexas de contagens, podemos usar uma forma específica para pedir agregações de resultados.
Por exemplo, qual a quantidade de internações por estado? Podemos obter este resultado especificando uma agregação.
Uma agregação para o ES precisa ser escrita seguindo um padrão. Veja abaixo:
agg_gravidez <- '{
"aggs": {
"a1": {
"terms": {
"field": "def_gravidez",
"size": 200
}
}
}
}'
Estamos criando um objeto chamado agg_gravidez
no R, que será usado na consulta ao ES. O que significa cada linha desse objeto?
aggs
: esse comando declara ao ES que você está requerindo uma agregação;a1
: nome da agregação, você pode modificar esse nome;terms
: isso declara ao ES que você quer fazer a agregação a partir de uma variável categórica, resultando na contagem de documentos. Não modifique essa linha;field
: esse será o campo que você deseja fazer a agregação, no nosso caso, por sigla de UF. Você pode modificar esta linha para outra variável categórica;size
: esse é o limite de resultados da agregação. O limite deste parâmetro é 10.000.Para buscarmos essa agregação no ES faremos uso da função criada anteriormente para trazê-la em formado de data.frame
:
df_agg_gravidez <- convertAggsToDF(size = 200, index="datasus-sim-dofet", connection=es_dofet, elastic_query=agg_gravidez)
head(df_agg_gravidez)
Perceba que estamos realizando contagens em aproximadamente 1 milhão registros. Fazer este tipo de contagem em um computador comum poderia durar horas. Utilizando a nossa infraestrutura, isso é feito em menos de 1 segundo.
O comando abaixo retorna um gráfico de barras gerado com base nos dados em df
.
# Basic barplot
ggplot(data=df_agg_gravidez, aes(x = reorder(key, doc_count), y = doc_count)) +
geom_bar(stat="identity", width=0.5, fill="steelblue") +
geom_text(aes(label=doc_count), vjust=-0.3, size=3.5)+
labs(x = "Tipo de Gravidez", y = "Número de Óbitos Fetais")
Na busca acima, temos o total de óbitos fetais por tipo de gravidez para todos os anos e todas as doenças, sem filtros. Podemos tornar essa busca mais precisa incluindo um filtro.
filter_ano_obito <- '{
"query": {
"bool": {
"must": [
{
"range": {
"ano_obito": { "from" : 2015, "to" : 2019 }
}
}
]
}
}'
Com o código acima, estamos criando um objeto chamado filter_ano_obito
no R, que será usado na consulta ao ES. O que significa cada linha desse objeto?
query
: como visto anteriormente, esse comando declara ao ES que você está requerindo uma busca;bool
: essa cláusula permite a construção de filtros que tenham múltiplos campos. Não modifique essa linha;range
: declara que o campo que trabalharemos será um range, nesse caso de anos delimitado inferiormente pela cláusula "from" e superiormente por "to";ano_obito
: Campo a ser filtrado.Com o filtro definido podemos criar a combinação do filtro de anos e uma agregação por codigo da UF da seguinte forma:
agg_uf_obito <- '{
"query": {
"bool": {
"must": [
{
"range": {
"ano_obito": { "from" : 2015, "to" : 2019 }
}
}
]
}
},
"aggs": {
"a1": {
"terms": {
"field": "res_SIGLA_UF",
"size": 27
}
}
}
}'
df_agg_uf <- convertAggsToDF(size=50, connection=es_dofet, index='datasus-sim-dofet', elastic_query = agg_uf_obito)
head(df_agg_uf)
Podemos agora fazer uma outra busca, dessa vez agregando por municípios e filtrando pelo campo uf_CODIGO_UF
= "SP" AND ESTCIV
= 1. Vejamos o resultado.
agg_mun_filtro_uf_estciv <- '{
"query": {
"bool": {
"must": [
{
"query_string": {
"query": "res_SIGLA_UF:SP AND ESTCIV:1"
}
}
]
}
},
"aggs": {
"a1": {
"terms": {
"field": "CODMUNRES",
"size": 6000
}
}
}
}'
df_agg_mun_uf_estciv <- convertAggsToDF(size=50, connection=es_dofet, index='datasus-sim-dofet', elastic_query=agg_mun_filtro_uf_estciv)
head(df_agg_mun_uf_estciv)
Fim do tutorial