Este relatório reúne, em uma única publicação, as duas etapas do processamento dos dados utilizados na atividade.
A primeira etapa constrói uma base diária consolidada, unindo informações epidemiológicas de dengue e dados climáticos. A segunda etapa parte dessa base já processada para gerar tabelas menores e mais leves, voltadas para análises por mês, por ano e por semana.
A separação foi mantida de forma proposital. No desenvolvimento
original, o processo foi dividido em dois scripts para facilitar a
organização, a depuração e a reutilização da base principal. Nesta
versão para o RPubs, os dois blocos aparecem no mesmo documento, mas a
lógica continua separada: primeiro a tabela grande é salva em arquivo
.csv; depois ela é recarregada para gerar os arquivos
derivados.
O fluxo utilizado foi:
dengue_clima_dataset.csv.Essa escolha evita misturar a etapa mais pesada de preparação dos dados com a etapa de análise/agregação. Além disso, caso seja necessário alterar apenas os subconjuntos, não é preciso reconstruir toda a base original novamente.
O código abaixo corresponde à primeira etapa do projeto. Ele lê os arquivos originais, trata problemas de nomes, datas e valores ausentes, junta as bases de dengue e clima e salva a tabela final processada.
pacotes <- c("tidyverse", "lubridate", "stringi", "zoo")
invisible(lapply(pacotes, function(pkg) {
if (!requireNamespace(pkg, quietly = TRUE)) install.packages(pkg)
library(pkg, character.only = TRUE)
}))
source_data_dir <- file.path(getwd(), "dados")
if (!dir.exists(source_data_dir)) {
stop("Não encontrei a pasta dados. Execute este script na raiz do projeto ou ajuste os diretórios.")
}
processed_dir <- file.path(source_data_dir, "processed")
dir.create(processed_dir, recursive = TRUE, showWarnings = FALSE)
output_file <- file.path(processed_dir, "dengue_clima_dataset.csv")
limite_percentual_ausente <- 60
# Funções auxiliares
normalize_column_name <- function(col) {
col %>% stringi::stri_trans_general("Latin-ASCII") %>% stringr::str_to_lower() %>%
stringr::str_replace_all("[^a-z0-9]+", "_") %>% stringr::str_replace_all("^_|_$", "")
}
normalize_text <- function(x) {
x %>% as.character() %>% stringi::stri_trans_general("Latin-ASCII") %>%
stringr::str_to_lower() %>% stringr::str_squish()
}
parse_data_flex <- function(x) {
x <- as.character(x)
d1 <- suppressWarnings(lubridate::ymd(x))
d2 <- suppressWarnings(lubridate::dmy(x))
dplyr::coalesce(d1, d2)
}
as_num <- function(x) {
y <- suppressWarnings(as.numeric(stringr::str_replace(as.character(x), ",", ".")))
y[y <= -9990] <- NA_real_
y
}
fill_rolling_mean <- function(x, width = 4, min_obs = 2) {
media_movel <- zoo::rollapplyr(
data = x,
width = width,
FUN = function(v) if (sum(!is.na(v)) >= min_obs) mean(v, na.rm = TRUE) else NA_real_,
partial = TRUE,
fill = NA_real_
)
ifelse(is.na(x), media_movel, x)
}
mean_na <- function(x) if (all(is.na(x))) NA_real_ else mean(x, na.rm = TRUE)
sum_na <- function(x) if (all(is.na(x))) NA_real_ else sum(x, na.rm = TRUE)
pick_col <- function(cols, pattern) {
achada <- cols[stringr::str_detect(cols, stringr::regex(pattern, ignore_case = TRUE))][1]
ifelse(is.na(achada), NA_character_, achada)
}
add_missing_cols <- function(df, cols) {
for (col in cols) if (!col %in% names(df)) df[[col]] <- NA
df
}
has_any_1 <- function(df, prefix) {
cols <- names(df)[stringr::str_detect(names(df), paste0("^", prefix))]
if (length(cols) == 0) return(rep(FALSE, nrow(df)))
rowSums(as.data.frame(lapply(df[cols], function(x) x == 1)), na.rm = TRUE) > 0
}
get_municip_map_df <- function(cod_municipios_unicos) {
municipios <- readr::read_csv(file.path(source_data_dir, "municipios.csv"), show_col_types = FALSE) %>%
select(codigo_ibge, nome, codigo_uf)
estados <- readr::read_csv(file.path(source_data_dir, "estados.csv"), show_col_types = FALSE) %>%
select(codigo_uf, uf)
municipios %>%
left_join(estados, by = "codigo_uf") %>%
mutate(codigo_municipio = codigo_ibge %/% 10) %>%
select(codigo_municipio, nome, uf) %>%
filter(codigo_municipio %in% cod_municipios_unicos)
}
# DENGBR
prep_dengbr <- function(df, df_cod_municip) {
df <- add_missing_cols(df, c("CLASSI_FIN", "EVOLUCAO", "DT_OBITO", "HOSPITALIZ", "DIABETES", "HIPERTENSA"))
df_base <- df %>%
left_join(df_cod_municip, by = c("ID_MUNICIP" = "codigo_municipio")) %>%
mutate(
data = parse_data_flex(DT_NOTIFIC),
uf = stringr::str_to_upper(stringr::str_squish(uf)),
municipio = normalize_text(nome),
caso_confirmado = CLASSI_FIN %in% c(10, 11, 12),
caso_hospitalizado = HOSPITALIZ == 1,
caso_obito = EVOLUCAO == 2 | !is.na(parse_data_flex(DT_OBITO)),
caso_diabetes = DIABETES == 1,
caso_hipertensao = HIPERTENSA == 1
) %>%
drop_na(data, uf, municipio)
df_base$tem_sinal_alarme <- has_any_1(df_base, "ALRM_")
df_base$tem_gravidade <- has_any_1(df_base, "GRAV_")
df_base %>%
mutate(caso_com_sinal_alarme = CLASSI_FIN == 11 | tem_sinal_alarme, caso_grave = CLASSI_FIN == 12 | tem_gravidade) %>%
group_by(data, uf, municipio) %>%
summarise(
casos_dengue = n(),
casos_confirmados = sum(caso_confirmado, na.rm = TRUE),
casos_hospitalizados = sum(caso_hospitalizado, na.rm = TRUE),
casos_obito = sum(caso_obito, na.rm = TRUE),
casos_com_sinais_alarme = sum(caso_com_sinal_alarme, na.rm = TRUE),
casos_graves = sum(caso_grave, na.rm = TRUE),
casos_com_diabetes = sum(caso_diabetes, na.rm = TRUE),
casos_com_hipertensao = sum(caso_hipertensao, na.rm = TRUE),
.groups = "drop"
)
}
# INMET
prep_inmet <- function(df, file_name) {
df <- df %>% select(!matches("^\\.\\.\\.|^X[0-9]+$|^$"))
names(df) <- make.unique(normalize_column_name(names(df)), sep = "_")
mapa <- c(
data = pick_col(names(df), "^data$"),
precipitacao_total = pick_col(names(df), "precipitacao_total"),
pressao_atmosferica = pick_col(names(df), "pressao_atmosferica_ao_nivel_da_estacao"),
radiacao_global = pick_col(names(df), "radiacao_global"),
temperatura_ar = pick_col(names(df), "temperatura_do_ar_bulbo_seco"),
temperatura_orvalho = pick_col(names(df), "temperatura_do_ponto_de_orvalho"),
umidade_relativa = pick_col(names(df), "umidade_relativa_do_ar"),
vento_velocidade = pick_col(names(df), "vento_velocidade_horaria")
)
if (is.na(mapa[["data"]])) stop(paste("Coluna de data não encontrada no arquivo", file_name))
df <- df %>% select(all_of(unique(na.omit(mapa))))
for (nome_final in names(mapa)) {
nome_original <- mapa[[nome_final]]
if (!is.na(nome_original) && nome_original %in% names(df)) df <- df %>% rename(!!nome_final := all_of(nome_original))
}
vars_clima <- c("precipitacao_total", "pressao_atmosferica", "radiacao_global", "temperatura_ar", "temperatura_orvalho", "umidade_relativa", "vento_velocidade")
df <- add_missing_cols(df, vars_clima) %>%
mutate(data = parse_data_flex(data), across(all_of(vars_clima), as_num)) %>%
arrange(data)
percentual_ausente <- df %>% summarise(across(all_of(vars_clima), ~ mean(is.na(.x)) * 100))
cols_preencher <- names(percentual_ausente)[unlist(percentual_ausente) <= limite_percentual_ausente]
df <- df %>% mutate(across(all_of(cols_preencher), fill_rolling_mean))
df_diario <- df %>%
group_by(data) %>%
summarise(
precipitacao_total = sum_na(precipitacao_total),
pressao_atmosferica = mean_na(pressao_atmosferica),
radiacao_global = mean_na(radiacao_global),
temperatura_ar = mean_na(temperatura_ar),
temperatura_orvalho = mean_na(temperatura_orvalho),
umidade_relativa = mean_na(umidade_relativa),
vento_velocidade = mean_na(vento_velocidade),
.groups = "drop"
) %>%
drop_na(data)
partes_nome <- stringr::str_split(file_name, "_", simplify = TRUE)
if (ncol(partes_nome) < 5) stop(paste("Nome do arquivo INMET fora do padrão esperado:", file_name))
uf <- partes_nome[1, 3]
municipio <- partes_nome[1, 5]
df_diario %>%
mutate(uf = stringr::str_to_upper(stringr::str_squish(uf)), municipio = normalize_text(municipio)) %>%
select(data, uf, municipio, everything())
}
cat("Lendo DENGBR...\n")
dengue_files <- file.path(source_data_dir, "DENGBR", c("DENGBR23.csv", "DENGBR24.csv", "DENGBR25.csv"))
cols_dengue <- c(
"DT_NOTIFIC", "ID_MUNICIP", "ID_AGRAVO", "CLASSI_FIN", "EVOLUCAO", "DT_OBITO", "HOSPITALIZ", "DIABETES", "HIPERTENSA",
"ALRM_HIPOT", "ALRM_PLAQ", "ALRM_VOM", "ALRM_SANG", "ALRM_HEMAT", "ALRM_ABDOM", "ALRM_LETAR", "ALRM_HEPAT", "ALRM_LIQ",
"GRAV_PULSO", "GRAV_CONV", "GRAV_ENCH", "GRAV_INSUF", "GRAV_TAQUI", "GRAV_EXTRE", "GRAV_HIPOT", "GRAV_HEMAT",
"GRAV_MELEN", "GRAV_METRO", "GRAV_SANG", "GRAV_AST", "GRAV_MIOC", "GRAV_CONSC", "GRAV_ORGAO"
)
dengue_raw <- purrr::map_dfr(
dengue_files,
~ readr::read_csv(.x, col_select = any_of(cols_dengue), show_col_types = FALSE)
)
cod_municipios_unicos <- unique(dengue_raw$ID_MUNICIP)
df_cod_municip <- get_municip_map_df(cod_municipios_unicos)
df_dengue <- prep_dengbr(dengue_raw, df_cod_municip)
cat("DENGBR processado:\n")
print(dplyr::glimpse(df_dengue))
cat("Lendo INMET...\n")
inmet_files <- list.files(
file.path(source_data_dir, "INMET"),
pattern = "\\.csv$",
recursive = TRUE,
full.names = TRUE,
ignore.case = TRUE
)
if (length(inmet_files) == 0) stop("Nenhum arquivo CSV do INMET foi encontrado em dados/INMET.")
df_inmet <- purrr::map_dfr(seq_along(inmet_files), function(i) {
path <- inmet_files[i]
arquivo <- tools::file_path_sans_ext(basename(path))
cat("Processando INMET", i, "de", length(inmet_files), "-", arquivo, "\n")
suppressMessages(
readr::read_delim(
path,
delim = ";",
skip = 8,
locale = readr::locale(encoding = "Latin1"),
show_col_types = FALSE,
trim_ws = TRUE,
name_repair = "unique_quiet",
progress = FALSE
)
) %>% prep_inmet(file_name = arquivo)
}) %>% arrange(uf, municipio, data)
cat("INMET processado:\n")
print(dplyr::glimpse(df_inmet))
cat("Criando tabela final...\n")
df_inmet_diario <- df_inmet %>%
group_by(uf, municipio, data) %>%
summarise(across(where(is.numeric), mean_na), .groups = "drop")
df_final <- df_inmet_diario %>%
left_join(df_dengue, by = c("data", "uf", "municipio")) %>%
mutate(
across(starts_with("casos_"), ~ tidyr::replace_na(.x, 0)),
ano = lubridate::year(data),
mes = lubridate::month(data),
dia = lubridate::day(data),
dia_da_semana = lubridate::wday(data, label = TRUE, abbr = FALSE)
) %>%
arrange(uf, municipio, data)
readr::write_csv(df_final, output_file)
cat("\nTabela final salva em:", output_file, "\n")
cat("Linhas:", nrow(df_final), "\n")
cat("Colunas:", ncol(df_final), "\n")
resumo_base <- tibble::tibble(
indicador = c("linhas", "colunas", "ufs", "municipios", "data_inicial", "data_final", "total_casos_dengue", "total_hospitalizados", "total_obitos", "total_casos_graves", "linhas_duplicadas_por_chave"),
valor = c(
nrow(df_final),
ncol(df_final),
n_distinct(df_final$uf),
n_distinct(paste(df_final$uf, df_final$municipio)),
as.character(min(df_final$data, na.rm = TRUE)),
as.character(max(df_final$data, na.rm = TRUE)),
sum(df_final$casos_dengue, na.rm = TRUE),
sum(df_final$casos_hospitalizados, na.rm = TRUE),
sum(df_final$casos_obito, na.rm = TRUE),
sum(df_final$casos_graves, na.rm = TRUE),
df_final %>% count(data, uf, municipio) %>% filter(n > 1) %>% nrow()
)
)
cat("\nResumo da base:\n")
print(resumo_base)
faltantes_final <- df_final %>%
summarise(across(everything(), ~ mean(is.na(.x)) * 100)) %>%
pivot_longer(everything(), names_to = "coluna", values_to = "percentual_faltante") %>%
arrange(desc(percentual_faltante))
cat("\nPercentual de faltantes por coluna:\n")
print(faltantes_final)
cat("\nProcessamento finalizado.\n")
A base consolidada foi salva em arquivo .csv para
funcionar como um ponto intermediário do processo.
Mesmo estando tudo em uma única publicação, essa leitura posterior foi mantida para deixar claro que, originalmente, a implementação foi organizada em duas etapas independentes. Assim, a segunda parte do código não depende diretamente dos objetos criados em memória na primeira parte; ela depende do arquivo processado que foi gerado.
O código abaixo lê a base diária
dengue_clima_dataset.csv e gera três tabelas derivadas:
dengue_mensal_municipio.csv: casos de dengue agregados
por município e mês.dengue_anual_municipio.csv: casos de dengue agregados
por município e ano.geral_semanal_completo.csv: visão semanal geral com
variáveis de dengue e clima.pacotes <- c("tidyverse", "lubridate")
invisible(lapply(pacotes, function(pkg) {
if (!requireNamespace(pkg, quietly = TRUE)) install.packages(pkg)
library(pkg, character.only = TRUE)
}))
# -----------------------------------------------------------------------------
# Script: create_dengue_subsets.R
# Objetivo:
# Ler a base diaria dengue_clima_dataset.csv e criar apenas 3 subsets leves:
# 1) dengue_mensal_municipio.csv -> dengue por municipio e mes
# 2) dengue_anual_municipio.csv -> dengue por municipio e ano
# 3) geral_semanal_completo.csv -> visao semanal geral com dengue + clima
# -----------------------------------------------------------------------------
# Esperado: executar na raiz do projeto, onde existe R_files/dados/processed.
source_data_dir <- file.path(getwd(), "R_files", "dados")
processed_dir <- file.path(source_data_dir, "processed")
# Fallback simples caso o script seja executado de dentro da pasta R_files.
if (!dir.exists(processed_dir)) {
alt_processed_dir <- file.path(getwd(), "dados", "processed")
if (dir.exists(alt_processed_dir)) {
processed_dir <- alt_processed_dir
} else {
stop("Nao encontrei a pasta processed. Execute este script na raiz do projeto ou ajuste os diretorios.")
}
}
input_file <- file.path(processed_dir, "dengue_clima_dataset.csv")
subsets_dir <- file.path(processed_dir, "subsets")
dir.create(subsets_dir, recursive = TRUE, showWarnings = FALSE)
if (!file.exists(input_file)) {
stop("Arquivo dengue_clima_dataset.csv nao encontrado em dados/processed.")
}
# Funcoes auxiliares para evitar NaN quando uma coluna esta toda vazia.
safe_sum <- function(x) {
if (all(is.na(x))) NA_real_ else sum(x, na.rm = TRUE)
}
safe_mean <- function(x) {
if (all(is.na(x))) NA_real_ else mean(x, na.rm = TRUE)
}
safe_max <- function(x) {
if (all(is.na(x))) NA_real_ else max(x, na.rm = TRUE)
}
cat("Lendo dataset processado...\n")
## Lendo dataset processado...
df_base <- readr::read_csv(input_file, show_col_types = FALSE) %>%
mutate(
data = as.Date(data),
ano = lubridate::year(data),
mes = lubridate::month(data),
mes_inicio = lubridate::floor_date(data, unit = "month"),
semana_inicio = lubridate::floor_date(data, unit = "week", week_start = 1),
ano_semana = lubridate::isoyear(data),
semana = lubridate::isoweek(data),
casos_dengue = as.numeric(casos_dengue)
)
# Somente colunas de dengue.
# Pelo dataset atual, essas colunas seguem o padrao casos_*.
dengue_cols <- names(df_base)[stringr::str_detect(names(df_base), "^casos_")]
if (length(dengue_cols) == 0) {
stop("Nenhuma coluna de dengue encontrada. Esperava colunas iniciando com 'casos_'.")
}
# Colunas climaticas esperadas no dataset final.
clima_cols <- intersect(
c(
"precipitacao_total",
"pressao_atmosferica",
"radiacao_global",
"temperatura_ar",
"temperatura_orvalho",
"umidade_relativa",
"vento_velocidade"
),
names(df_base)
)
clima_media_cols <- setdiff(clima_cols, "precipitacao_total")
cat("Colunas de dengue encontradas:\n")
## Colunas de dengue encontradas:
print(dengue_cols)
## [1] "casos_dengue" "casos_confirmados"
## [3] "casos_hospitalizados" "casos_obito"
## [5] "casos_com_sinais_alarme" "casos_graves"
## [7] "casos_com_diabetes" "casos_com_hipertensao"
cat("Colunas climaticas encontradas:\n")
## Colunas climaticas encontradas:
print(clima_cols)
## [1] "precipitacao_total" "pressao_atmosferica" "radiacao_global"
## [4] "temperatura_ar" "temperatura_orvalho" "umidade_relativa"
## [7] "vento_velocidade"
# -----------------------------------------------------------------------------
# 1. Subset mensal orientado a municipio - somente informacoes de dengue
# -----------------------------------------------------------------------------
dengue_mensal_municipio <- df_base %>%
group_by(uf, municipio, ano, mes, mes_inicio) %>%
summarise(
dias_monitorados = n_distinct(data),
dias_com_casos = sum(casos_dengue > 0, na.rm = TRUE),
media_diaria_casos_dengue = safe_mean(casos_dengue),
pico_diario_casos_dengue = safe_max(casos_dengue),
across(all_of(dengue_cols), safe_sum),
.groups = "drop"
) %>%
arrange(uf, municipio, ano, mes)
readr::write_csv(
dengue_mensal_municipio,
file.path(subsets_dir, "dengue_mensal_municipio.csv")
)
# -----------------------------------------------------------------------------
# 2. Subset anual orientado a municipio - somente informacoes de dengue
# -----------------------------------------------------------------------------
dengue_anual_municipio <- df_base %>%
group_by(uf, municipio, ano) %>%
summarise(
dias_monitorados = n_distinct(data),
dias_com_casos = sum(casos_dengue > 0, na.rm = TRUE),
meses_monitorados = n_distinct(mes_inicio),
media_diaria_casos_dengue = safe_mean(casos_dengue),
pico_diario_casos_dengue = safe_max(casos_dengue),
across(all_of(dengue_cols), safe_sum),
.groups = "drop"
) %>%
arrange(uf, municipio, ano)
readr::write_csv(
dengue_anual_municipio,
file.path(subsets_dir, "dengue_anual_municipio.csv")
)
# -----------------------------------------------------------------------------
# 3. Subset semanal geral completo - dengue + clima
#
# Regra usada:
# - Colunas casos_*: soma semanal geral.
# - precipitacao_total: primeiro soma por municipio/semana, depois tira a media
# municipal no agregado geral, para evitar um total nacional artificial gigante.
# - Demais variaveis climaticas: media semanal geral.
# -----------------------------------------------------------------------------
base_semanal_municipio_temp <- df_base %>%
group_by(uf, municipio, semana_inicio, ano_semana, semana) %>%
summarise(
dias_monitorados = n_distinct(data),
across(all_of(dengue_cols), safe_sum),
across(any_of("precipitacao_total"), safe_sum),
across(all_of(clima_media_cols), safe_mean),
.groups = "drop"
)
geral_semanal_completo <- base_semanal_municipio_temp %>%
group_by(semana_inicio, ano_semana, semana) %>%
summarise(
ufs_monitoradas = n_distinct(uf),
municipios_monitorados = n_distinct(paste(uf, municipio)),
dias_municipio_monitorados = sum(dias_monitorados, na.rm = TRUE),
across(all_of(dengue_cols), safe_sum),
across(
any_of("precipitacao_total"),
safe_mean,
.names = "{.col}_media_municipal"
),
across(
all_of(clima_media_cols),
safe_mean,
.names = "{.col}_media"
),
.groups = "drop"
) %>%
arrange(semana_inicio)
readr::write_csv(
geral_semanal_completo,
file.path(subsets_dir, "geral_semanal_completo.csv")
)
cat("\nSubsets salvos em:", subsets_dir, "\n")
##
## Subsets salvos em: C:/Users/pedro/OneDrive/Documentos/R_files/dados/processed/subsets
cat("Arquivos gerados:\n")
## Arquivos gerados:
cat("- dengue_mensal_municipio.csv:", nrow(dengue_mensal_municipio), "linhas\n")
## - dengue_mensal_municipio.csv: 20473 linhas
cat("- dengue_anual_municipio.csv:", nrow(dengue_anual_municipio), "linhas\n")
## - dengue_anual_municipio.csv: 1726 linhas
cat("- geral_semanal_completo.csv:", nrow(geral_semanal_completo), "linhas\n")
## - geral_semanal_completo.csv: 158 linhas
cat("\nProcessamento finalizado.\n")
##
## Processamento finalizado.
A solução foi organizada dessa forma para manter o código mais fácil de entender e revisar. A primeira parte concentra a limpeza, padronização e integração das bases. A segunda parte utiliza a base já tratada para gerar tabelas menores, mais adequadas para visualizações e análises específicas.
Essa divisão também facilita a manutenção: se houver alguma mudança na forma de agregar os dados, basta modificar a segunda etapa. Se houver mudança nos dados originais ou na estratégia de limpeza, a alteração fica concentrada na primeira etapa.