Neste notebook, iremos demonstrar como acessar os dados do Sistema de Informações Hospitalares (SIH) indexados pela Plataforma de Ciência de Dados aplicada à Saúde (PCDaS) através do R.
Os dados do SIH estão disponíveis em um índice do ElasticSearch (ES), que contém todos os registros individuais Autorizações de Internação Hospitalar (AIH).
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.
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_sih
) para acesso ao índice do ES contendo os dados do SIH.
es_sih <- 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_sih)
Podemos executar buscas nos dados e ver os documentos (registros) do índice (banco do SIH) 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 SIH. Em seguida faremos queries com as funções criadas.
elastic::count(es_sih, "datasus-sih")
getColnames <- function(colunas){
colunas <- lapply(X = colunas, FUN = function(t) gsub(pattern = "_source.", replacement = "", x = t, fixed = TRUE))
return(colunas)
}
convertSearchToDF <- function(index, connection, size=100, 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(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-sih", connection=es_sih, 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_uf <- '{
"aggs": {
"a1": {
"terms": {
"field": "res_SIGLA_UF.keyword",
"size": 27
}
}
}
}'
Estamos criando um objeto chamado agg_uf
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. Como temos 27 estados, podemos especificar o tamanho 27. Se colocarmos um tamanho maior, não irá causar nenhum erro. Se o campo de agregação fosse o nome de municípios, devemos usar um número maior (exemplo: 6.000) para obter os resultados de todos os municípios. 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_uf <- convertAggsToDF(size = 27, index="datasus-sih", connection=es_sih, elastic_query=agg_uf)
head(df_agg_uf)
Perceba que estamos realizando contagens em mais de 150 milhões registros. Fazer este tipo de contagem em um computador comum poderia durar horas ou dias. 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_uf, aes(x = reorder(key, doc_count), y = doc_count)) +
geom_bar(stat = "identity") +
labs(x = "UF", y = "Internações")
Na busca acima, temos o total de óbitos por estado para todos os anos e todas as doenças, sem filtros. Podemos tornar essa busca mais precisa incluindo um filtro.
filter_causa_ano <- '{
"query": {
"bool": {
"must": [
{
"range": {
"ANO_CMPT": { "from" : 2010, "to" : 2012 }
}
}
]
}
}
}'
Com o código acima, estamos criando um objeto chamado agg_uf_filtro_ano
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_CMPT
: Campo a ser filtrado.Com o filtro definido podemos criar a combinação do filtro de anos e a agregação por UF da seguinte forma:
agg_uf_ano <- '{
"query": {
"bool": {
"must": [
{
"range": {
"ANO_CMPT": { "from" : 2010, "to" : 2012 }
}
}
]
}
},
"aggs": {
"a1": {
"terms": {
"field": "res_SIGLA_UF.keyword",
"size": 27
}
}
}
}'
df_agg_ano_uf <- convertAggsToDF(size = 27, index="datasus-sih", connection=es_sih, elastic_query=agg_uf_ano)
head(df_agg_ano_uf)
Podemos agora fazer uma outra busca, dessa vez agregando por municípios e filtrando pelo campo DIAG_PRINC
= "A09". Vejamos o resultado.
agg_mun_filtro_diag <- '{
"query": {
"bool": {
"must": [
{
"query_string": {
"query": "DIAG_PRINC:A09"}
}
]
}
},
"aggs": {
"a1": {
"terms": {
"field": "res_MUNNOME",
"size": 6000
}
}
}
}'
df_agg_mun_diag <- convertAggsToDF(size=6000, index='datasus-sih', connection=es_sih, elastic_query=agg_mun_filtro_diag)
head(df_agg_mun_diag)
Fim do tutorial