Sistema de Informação sobre Mortalidade - Declaração de Óbitos Fetais (SIM-DOFET)

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.

Pacotes necessários

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.

In [1]:
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.

In [2]:
loadlibrary("elastic")
Loading required package: 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.

In [3]:
packages <- c("dplyr", "curl", "jsonlite", "ggplot2", "getPass")
lapply(packages, loadlibrary)

options(repr.plot.width=15, repr.plot.height=10)
Loading required package: dplyr


Attaching package: ‘dplyr’


The following objects are masked from ‘package:elastic’:

    count, explain


The following objects are masked from ‘package:stats’:

    filter, lag


The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union


Loading required package: curl

Using libcurl 7.58.0 with OpenSSL/1.1.1

Loading required package: jsonlite


Attaching package: ‘jsonlite’


The following object is masked from ‘package:elastic’:

    validate


Loading required package: ggplot2

Loading required package: getPass

  1. NULL
  2. NULL
  3. NULL
  4. NULL
  5. NULL

Acesso ao ElasticSearch

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

In [18]:
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: ")
Digite seu usuário da PCDaS: login.teste
Digite sua senha da PCDaS: ··········

Em seguida, criamos um objeto (es_dofet) para acesso ao índice do ES contendo os dados do SIM-DOFET.

In [5]:
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.

In [6]:
print(es_dofet)
<Elasticsearch Connection> 
  transport:  https 
  host:       dados-pcdas.icict.fiocruz.br 
  port:       443 
  path:       NULL 
  username:   gabriel.souto 
  password:   <secret> 
  errors:     simple 
  headers (names):   
  cainfo:  NULL 
  ignore ES version?:  FALSE 

Fazendo buscas nos dados

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.

In [7]:
elastic::count(es_dofet, "datasus-sim-dofet")
857664
In [8]:
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.

In [9]:
tudo <- '{
  "query": {
    "match_all": {}
  }
}'
In [10]:
df <- convertSearchToDF(size=100, index="datasus-sim-dofet", connection=es_dofet, elastic_query = tudo)
head(df)
A data.frame: 6 × 128
CONTADORTIPOBITODTOBITOHORAOBITONATURALDTNASCIDADESEXORACACORESTCIVres_coordenadasocor_coordenadasres_REGIAOocor_REGIAOcausabas_capitulocausabas_grupocausabas_categoriacausabas_subcategoriadef_cbocontador
<chr><chr><chr><chr><chr><chr><lgl><chr><chr><lgl><chr><chr><chr><chr><chr><chr><chr><chr><chr><chr>
185771030420081051835NA NA19NA-22.523,-46.65 -22.906,-47.061SudesteSudesteXVI. Algumas afec originadas no período perinatalTranst respirat e cardiovasc específ per perinatalP20 Hipoxia intra-uterina P20.9 Hipoxia intra-uterina NE COZINHEIRO GERAL NA
2NA 124092004NA 83524092004NA29NA-22.457,-47.53 -22.411,-47.561SudesteSudesteXVI. Algumas afec originadas no período perinatalFet rec-nasc afet fat mat e compl grav, trab partoP02 Fet rec-nasc afet compl plac cord umb membrP02.2 Fet rec-nasc afet out anor morf func plac NENA 8013
385791030420082300843NA NA19NA-28.712,-51.935-28.712,-51.935Sul Sul XVI. Algumas afec originadas no período perinatalOutros transtornos originados no período perinatalP95 Morte fetal de causa NE P95 Morte fetal de causa NE ALIMENTADOR DE LINHA DE PRODUCAONA
485801030420081930NA NA NA19NA-2.06,-47.553 -1.297,-47.922 Norte Norte XVI. Algumas afec originadas no período perinatalFet rec-nasc afet fat mat e compl grav, trab partoP02 Fet rec-nasc afet compl plac cord umb membrP02.5 Fet rec-nasc afet outr compr cord umbilical NA NA
5NA 112122004NA 83512122004NA19NA-23.548,-46.636-23.548,-46.636SudesteSudesteXVI. Algumas afec originadas no período perinatalFet rec-nasc afet fat mat e compl grav, trab partoP02 Fet rec-nasc afet compl plac cord umb membrP02.2 Fet rec-nasc afet out anor morf func plac NENA 8018
685821030420080928831NA NA29NA-19.932,-44.054-19.817,-43.956SudesteSudesteXVI. Algumas afec originadas no período perinatalOutros transtornos originados no período perinatalP95 Morte fetal de causa NE P95 Morte fetal de causa NE NA NA

Agregando resultados

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:

In [11]:
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:

In [12]:
df_agg_gravidez <- convertAggsToDF(size = 200, index="datasus-sim-dofet", connection=es_dofet, elastic_query=agg_gravidez)
head(df_agg_gravidez)
A data.frame: 4 × 2
keydoc_count
<chr><int>
1Única 715090
2Ignorado 100250
3Dupla 40344
4Tripla ou mais 1980

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.

In [13]:
# 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")

Agregando com filtros

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:

In [14]:
agg_uf_obito <- '{
    "query": {
        "bool": {
          "must": [
            {
              "range": {
                    "ano_obito": { "from" : 2015, "to" : 2019 }
              }
            }
          ]
        }
    },
    "aggs": {
        "a1": {
            "terms": {
                "field": "res_SIGLA_UF",
                "size": 27
            }
        }
    }
}'
In [15]:
df_agg_uf <- convertAggsToDF(size=50, connection=es_dofet, index='datasus-sim-dofet', elastic_query = agg_uf_obito)
head(df_agg_uf)
A data.frame: 6 × 2
keydoc_count
<chr><int>
1SP26843
2BA14711
3MG13859
4RJ13845
5PA 8023
6PE 7892

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.

In [16]:
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
            }
        }
    }
}'
In [17]:
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)
A data.frame: 6 × 2
keydoc_count
<chr><int>
13513002
23515002
33550302
43513801
53522501
63525301

Fim do tutorial