Análise Exploratória de Séries Temporais para Previsão de Dengue

Fundamentos Estatísticos e Engenharia de Atributos

Autores

UIVS - Unidade de Inteligência em Vigilância em Saúde

Caio Sain Vallio

Data de Publicação

11/01/2026

1 Introdução

1.1 Objetivo do Relatório

Este relatório apresenta uma análise exploratória abrangente dos dados de série temporal de dengue no Estado de São Paulo, com os seguintes objetivos:

  1. Compreender a estrutura dos dados utilizando os módulos R do projeto
  2. Avaliar a qualidade dos dados antes de qualquer modelagem
  3. Identificar padrões temporais (tendência, sazonalidade, ciclos)
  4. Demonstrar a engenharia de atributos implementada no projeto
  5. Estabelecer fundamentos estatísticos para modelos de forecast
Base para Modelagem

Este documento serve como base sólida para posterior aplicação e estudo de modelos de previsão (SARIMA, Prophet, ML, etc.).

1.2 Visão Geral dos Dados

Os dados utilizados compreendem:

  • Fonte: SINAN - Secretaria de Saúde do Estado de São Paulo
  • Período: 2014 a 2025
  • Frequência: Semanal (semanas epidemiológicas)
  • Granularidade: Agregado por Estado de São Paulo
  • Variável alvo: Incidência estimada por 100 mil habitantes (est_inc100k)
  • Covariáveis climáticas: Temperatura e precipitação

2 Carregamento e Preparação dos Dados

2.1 Módulo R/data/loader.R

O módulo loader.R fornece funções para carregar e agregar dados. Vamos demonstrar seu uso:

2.1.1 Função load_raw_data()

Carrega os dados brutos do arquivo .RData que contém informações por município.

Mostrar código
# Carregar dados brutos
df_raw <- load_raw_data()
#> [2026-01-11 14:04:34] INFO: Carregando dados de: /Users/caiosainvallio/ses-sp/forecast_dengue/data/raw/dengue.RData 
#> [2026-01-11 14:04:34] INFO: Dados carregados: 248865 linhas, 23 colunas
Mostrar código
# Estrutura dos dados brutos
cat("Dimensões:", nrow(df_raw), "linhas x", ncol(df_raw), "colunas\n\n")
#> Dimensões: 248865 linhas x 23 colunas
Mostrar código
cat("Variáveis disponíveis:\n")
#> Variáveis disponíveis:
Mostrar código
names(df_raw)
#>  [1] "data_iniSE"      "casos"           "casos_est"       "LI"             
#>  [5] "LS"              "LIb"             "LSb"             "ID_MN_RESI"     
#>  [9] "COD_7_mun"       "MUNICIPIO"       "COD_17DRS"       "17DRS_NOME_2022"
#> [13] "COD_GVE"         "GVE_NOME"        "ano"             "pop_total"      
#> [17] "lat"             "lon"             "mean_temp"       "mean_max_temp"  
#> [21] "mean_min_temp"   "mean_precip"     "Rt"

2.1.2 Função aggregate_state()

Agrega os dados dos municípios para o nível estadual, calculando:

  • Soma de casos e população
  • Média ponderada de incidência
  • Média de variáveis climáticas
Mostrar código
# Agregar para nivel estadual
df_estado <- aggregate_state(df_raw)
#> [2026-01-11 14:04:34] INFO: Agregando dados por estado... 
#> [2026-01-11 14:04:34] INFO: Dados agregados: 626 semanas
Mostrar código
# Visualizar estrutura
glimpse(df_estado)
#> Rows: 626
#> Columns: 12
#> $ data_iniSE    <date> 2014-01-05, 2014-01-12, 2014-01-19, 2014-01-26, 2014-02…
#> $ casos         <dbl> 1732, 2109, 2491, 3187, 3816, 5453, 6327, 6241, 7507, 99…
#> $ casos_est     <dbl> 1732, 2109, 2491, 3187, 3816, 5453, 6327, 6241, 7507, 99…
#> $ pop_total     <dbl> 36941862, 37276313, 37801725, 37638828, 37450990, 381143…
#> $ p_inc100k     <dbl> 4.688448, 5.657748, 6.589646, 8.467320, 10.189317, 14.30…
#> $ est_inc100k   <dbl> 4.688448, 5.657748, 6.589646, 8.467320, 10.189317, 14.30…
#> $ mean_temp     <dbl> 25.50957, 24.76327, 25.55820, 26.54986, 28.22553, 26.567…
#> $ mean_max_temp <dbl> 30.13921, 29.25627, 30.32381, 32.23857, 33.83009, 31.150…
#> $ mean_min_temp <dbl> 20.87994, 20.27026, 20.79259, 20.86114, 22.62097, 21.984…
#> $ mean_precip   <dbl> 3.21975684, 6.82361516, 2.51481481, 0.37228571, 0.210334…
#> $ semana        <dbl> 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, …
#> $ ano           <dbl> 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 20…

2.2 Módulo R/data/preprocess.R

O módulo preprocess.R aplica transformações essenciais aos dados.

2.2.1 Função preprocess_data()

Pipeline completo que:

  1. Remove semana 53 (incompleta em alguns anos)
  2. Preenche semanas faltantes
  3. Imputa NAs em covariáveis climáticas
  4. Adiciona transformação logarítmica do alvo
  5. Cria índice temporal
Mostrar código
# Preprocessar dados
df <- preprocess_data(df_estado)
#> [2026-01-11 14:04:34] INFO: ========== INICIO: Preprocessamento ========== 
#> [2026-01-11 14:04:34] INFO: Removidas 3 linhas da semana 53 
#> [2026-01-11 14:04:34] WARN: Semanas faltantes detectadas: 2 
#> [2026-01-11 14:04:34] WARN: ATENCAO: Alvo nao sera imputado (sera mantido como NA) 
#> [2026-01-11 14:04:34] INFO: Imputados 8 NAs em mean_temp (fill) 
#> [2026-01-11 14:04:34] INFO: Imputados 8 NAs em mean_max_temp (fill) 
#> [2026-01-11 14:04:34] INFO: Imputados 8 NAs em mean_min_temp (fill) 
#> [2026-01-11 14:04:34] INFO: Imputados 8 NAs em mean_precip (fill) 
#> [2026-01-11 14:04:34] INFO: Dados preprocessados: 625 linhas 
#> [2026-01-11 14:04:34] INFO: ========== FIM: Preprocessamento ==========
Mostrar código
# Resumo apos preprocessamento
cat(
    "Período:",
    format(min(df$data_iniSE), "%d/%m/%Y"), "a",
    format(max(df$data_iniSE), "%d/%m/%Y"), "\n"
)
#> Período: 05/01/2014 a 21/12/2025
Mostrar código
cat("Total de semanas:", nrow(df), "\n")
#> Total de semanas: 625
Mostrar código
cat("Variáveis:", ncol(df), "\n")
#> Variáveis: 14
Mostrar código
df |>
    select(est_inc100k, mean_temp, mean_precip, y_log) |>
    summary() |>
    kable(digits = 2) |>
    kable_styling(bootstrap_options = c("striped", "hover"))
Tabela 1: Resumo estatístico das principais variáveis
est_inc100k mean_temp mean_precip y_log
Min. : 1.577 Min. :14.12 Min. : 0.0000 Min. :0.9466
1st Qu.: 5.220 1st Qu.:20.39 1st Qu.: 0.7111 1st Qu.:1.8277
Median : 12.022 Median :22.73 Median : 2.4925 Median :2.5666
Mean : 39.308 Mean :22.26 Mean : 3.4504 Mean :2.8261
3rd Qu.: 37.794 3rd Qu.:24.17 3rd Qu.: 5.2889 3rd Qu.:3.6582
Max. :519.811 Max. :29.06 Max. :24.2985 Max. :6.2554
NA's :2 NA NA NA's :2

3 Controle de Qualidade dos Dados

3.1 Módulo R/data/quality.R

O módulo quality.R implementa verificações sistemáticas de qualidade dos dados, essenciais antes de qualquer análise ou modelagem.

3.1.1 Função run_data_quality()

Executa todas as checagens de qualidade e retorna um relatório estruturado:

Mostrar código
# Executar checagens de qualidade
dq_report <- run_data_quality(df)
#> [2026-01-11 14:04:34] INFO: ========== INICIO: Controle de Qualidade de Dados ========== 
#> [2026-01-11 14:04:34] WARN: Variaveis com NAs: casos, casos_est, pop_total, p_inc100k, est_inc100k, y_log 
#> [2026-01-11 14:04:34] INFO: Duplicatas: OK (nenhuma) 
#> [2026-01-11 14:04:34] INFO: Ordem temporal: OK 
#> [2026-01-11 14:04:34] INFO: Continuidade temporal: OK 
#> [2026-01-11 14:04:34] INFO: Faixas plausiveis: OK 
#> [2026-01-11 14:04:34] INFO: Todas as checagens de qualidade passaram 
#> [2026-01-11 14:04:34] INFO: ========== FIM: Controle de Qualidade de Dados ==========
Mostrar código
# Imprimir relatorio
print(dq_report)
#> 
#> ========================================
#>     RELATORIO DE QUALIDADE DE DADOS    
#> ========================================
#> 
#> Data: 2026-01-11 14:04:34
#> Registros: 625 linhas, 14 colunas
#> 
#> Status Geral: [OK] PASSOU
#> 
#> Checagens:
#> -----------------------------------------
#>   [OK] completeness
#>   [OK] duplicates
#>   [OK] temporal
#>   [OK] ranges

3.1.2 Detalhamento das Checagens

Mostrar código
data.frame(
    Checagem = c(
        "check_completeness()",
        "check_duplicates()",
        "check_temporal_order()",
        "check_plausible_ranges()"
    ),
    Descricao = c(
        "Verifica NAs por variável (crítico se > 50%)",
        "Identifica datas duplicadas na série",
        "Valida ordenação cronológica e gaps",
        "Confirma valores dentro de faixas plausíveis"
    ),
    Status = c(
        if (dq_report$checks$completeness$passed) "✓ OK" else "✗ Atenção",
        if (dq_report$checks$duplicates$passed) "✓ OK" else "✗ Atenção",
        if (dq_report$checks$temporal$passed) "✓ OK" else "✗ Atenção",
        if (dq_report$checks$ranges$passed) "✓ OK" else "✗ Atenção"
    )
) |>
    kable() |>
    kable_styling(bootstrap_options = c("striped", "hover"))
Tabela 2: Detalhamento das verificações de qualidade dos dados
Checagem Descricao Status
check_completeness() Verifica NAs por variável (crítico se > 50%) ✓ OK
check_duplicates() Identifica datas duplicadas na série ✓ OK
check_temporal_order() Valida ordenação cronológica e gaps ✓ OK
check_plausible_ranges() Confirma valores dentro de faixas plausíveis ✓ OK

3.1.3 Verificação de Completude

Mostrar código
# Calcular proporcao de NAs
na_props <- sapply(df, function(x) mean(is.na(x))) * 100
na_df <- data.frame(
    variavel = names(na_props),
    proporcao = as.numeric(na_props)
) |>
    filter(proporcao > 0) |>
    arrange(desc(proporcao))

if (nrow(na_df) > 0) {
    ggplot(na_df, aes(x = reorder(variavel, proporcao), y = proporcao)) +
        geom_col(fill = cores["tendencia"], alpha = 0.8) +
        coord_flip() +
        labs(
            title = "Valores Faltantes por Variável",
            x = NULL,
            y = "% de NAs"
        ) +
        scale_y_continuous(labels = function(x) paste0(x, "%"))
} else {
    cat("Nenhum valor faltante encontrado nas variáveis principais.")
}
Figura 1: Proporção de valores faltantes por variável

4 Análise Exploratória da Série Temporal

4.1 Visualização da Série de Incidência

Mostrar código
p <- ggplot(df, aes(x = data_iniSE, y = est_inc100k)) +
    geom_line(color = cores["observado"], linewidth = 0.8) +
    geom_smooth(
        method = "loess",
        span = 0.1,
        se = TRUE,
        color = cores["tendencia"],
        linewidth = 1,
        alpha = 0.2
    ) +
    labs(
        title = "Incidência de Dengue - Estado de São Paulo",
        subtitle = "Linha preta = Observado | Linha vermelha = Tendência suavizada (LOESS)",
        x = "Data",
        y = "Incidência por 100 mil hab."
    ) +
    scale_x_date(date_breaks = "1 year", date_labels = "%Y") +
    scale_y_continuous(
        labels = comma_format(big.mark = ".", decimal.mark = ",")
    )

ggplotly(p, tooltip = c("x", "y")) |>
    layout(
        hovermode = "x unified",
        autosize = TRUE,
        width = NULL,
        height = 450
    )
Figura 2: Série temporal da incidência de dengue no Estado de São Paulo
Observações Principais
  • Sazonalidade evidente: Picos anuais consistentes durante o verão (dez-mar)
  • Variabilidade interanual: Intensidade dos surtos varia significativamente entre anos
  • Mudança de regime em 2024: Surto de magnitude sem precedentes na série histórica

4.2 Decomposição STL

A decomposição STL (Seasonal and Trend decomposition using Loess) separa a série em três componentes:

  1. Tendência (\(T_t\)): Evolução de longo prazo
  2. Sazonalidade (\(S_t\)): Padrão repetitivo anual (período = 52 semanas)
  3. Resíduo (\(R_t\)): Variação não explicada

\[Y_t = T_t + S_t + R_t\]

Mostrar código
# Remover NAs e criar objeto ts
df_clean <- df |> filter(!is.na(est_inc100k))
y_ts <- ts(df_clean$est_inc100k, frequency = 52)

# Decomposicao STL
decomp <- stl(y_ts, s.window = "periodic", robust = TRUE)

# Plot da decomposicao
autoplot(decomp) +
    labs(title = "Decomposição STL da Incidência de Dengue") +
    theme_minimal()
Figura 3: Decomposição STL da série de incidência de dengue
Mostrar código
# Calcular variancias
var_trend <- var(decomp$time.series[, "trend"], na.rm = TRUE)
var_season <- var(decomp$time.series[, "seasonal"], na.rm = TRUE)
var_remainder <- var(decomp$time.series[, "remainder"], na.rm = TRUE)
var_total <- var(df_clean$est_inc100k, na.rm = TRUE)

data.frame(
    Componente = c("Tendência", "Sazonalidade", "Resíduo"),
    Variancia = c(var_trend, var_season, var_remainder),
    Proporcao = c(var_trend, var_season, var_remainder) / var_total * 100
) |>
    mutate(
        Variancia = round(Variancia, 2),
        Proporcao = sprintf("%.1f%%", Proporcao)
    ) |>
    kable(col.names = c("Componente", "Variância", "% do Total")) |>
    kable_styling(bootstrap_options = c("striped", "hover"))
Tabela 3: Contribuição de cada componente para a variância total
Componente Variância % do Total
Tendência 58.86 1.0%
Sazonalidade 694.91 12.3%
Resíduo 4081.68 72.4%

4.3 Análise de Estacionariedade

Para modelagem com ARIMA, é fundamental verificar se a série é estacionária (média e variância constantes no tempo).

4.3.1 Teste Augmented Dickey-Fuller (ADF)

  • H0: A série tem raiz unitária (não estacionária)
  • H1: A série é estacionária
Mostrar código
# Teste ADF na serie original
adf_original <- adf.test(na.omit(df$est_inc100k))

cat("=== Teste ADF - Série Original ===\n")
#> === Teste ADF - Série Original ===
Mostrar código
cat("Estatística ADF:", round(adf_original$statistic, 4), "\n")
#> Estatística ADF: -5.5614
Mostrar código
cat("P-valor:", round(adf_original$p.value, 4), "\n")
#> P-valor: 0.01
Mostrar código
cat("Conclusão:", ifelse(adf_original$p.value < 0.05,
    "Série estacionária (rejeita H0)",
    "Série NÃO estacionária (não rejeita H0)"
), "\n")
#> Conclusão: Série estacionária (rejeita H0)

4.3.2 Teste KPSS

  • H0: A série é estacionária
  • H1: A série tem raiz unitária
Mostrar código
# Teste KPSS
kpss_original <- kpss.test(na.omit(df$est_inc100k))

cat("=== Teste KPSS - Série Original ===\n")
#> === Teste KPSS - Série Original ===
Mostrar código
cat("Estatística KPSS:", round(kpss_original$statistic, 4), "\n")
#> Estatística KPSS: 1.1404
Mostrar código
cat("P-valor:", round(kpss_original$p.value, 4), "\n")
#> P-valor: 0.01
Mostrar código
cat("Conclusão:", ifelse(kpss_original$p.value < 0.05,
    "Série NÃO estacionária (rejeita H0)",
    "Série estacionária (não rejeita H0)"
), "\n")
#> Conclusão: Série NÃO estacionária (rejeita H0)

4.3.3 Efeito da Diferenciação Sazonal

Aplicando diferenciação sazonal (\(\nabla_{52} Y_t = Y_t - Y_{t-52}\)) para remover a sazonalidade:

Mostrar código
y_clean <- na.omit(df$est_inc100k)
y_diff <- diff(y_clean, lag = 52)

# Criar dataframe para plot
df_diff <- data.frame(
    t = 1:length(y_diff),
    valor = y_diff
)

p1 <- ggplot(df_clean, aes(x = data_iniSE, y = est_inc100k)) +
    geom_line(color = cores["observado"]) +
    labs(title = "Série Original", x = NULL, y = "Incidência")

p2 <- ggplot(df_diff, aes(x = t, y = valor)) +
    geom_line(color = cores["tendencia"]) +
    geom_hline(yintercept = 0, linetype = "dashed", alpha = 0.5) +
    labs(title = "Série Diferenciada (lag = 52)", x = "Tempo", y = "Diferença")

p1 / p2 + plot_annotation(
    title = "Efeito da Diferenciação Sazonal"
)
Figura 4: Série original vs. série diferenciada sazonalmente
Mostrar código
# Testar estacionariedade apos diferenciacao
adf_diff <- adf.test(y_diff)

cat("=== Teste ADF - Série Diferenciada ===\n")
#> === Teste ADF - Série Diferenciada ===
Mostrar código
cat("Estatística ADF:", round(adf_diff$statistic, 4), "\n")
#> Estatística ADF: -5.0636
Mostrar código
cat("P-valor:", round(adf_diff$p.value, 4), "\n")
#> P-valor: 0.01
Mostrar código
cat("Conclusão:", ifelse(adf_diff$p.value < 0.05,
    "Série estacionária após diferenciação sazonal",
    "Ainda não estacionária"
), "\n")
#> Conclusão: Série estacionária após diferenciação sazonal

4.4 Análise de Autocorrelação

4.4.1 ACF - Função de Autocorrelação

A ACF mede a correlação entre \(Y_t\) e \(Y_{t-k}\) para diferentes lags \(k\).

Mostrar código
acf_result <- acf(y_clean, lag.max = 104, plot = FALSE)

acf_df <- data.frame(
    lag = acf_result$lag[-1],
    acf = acf_result$acf[-1]
)

# Limite de significancia
n <- length(y_clean)
ci <- qnorm(0.975) / sqrt(n)

p_acf <- ggplot(acf_df, aes(x = lag, y = acf)) +
    geom_hline(yintercept = 0, color = "gray50") +
    geom_hline(yintercept = c(-ci, ci), linetype = "dashed", color = "blue") +
    geom_segment(aes(xend = lag, yend = 0), color = cores["observado"]) +
    geom_point(color = cores["observado"], size = 1) +
    geom_vline(xintercept = 52, linetype = "dotted", color = "red", linewidth = 1) +
    annotate("text",
        x = 54, y = 0.6, label = "Lag 52\n(1 ano)",
        color = "red", hjust = 0, size = 3
    ) +
    labs(
        title = "Função de Autocorrelação (ACF)",
        subtitle = "Linhas azuis: intervalo de confiança 95%",
        x = "Lag (semanas)",
        y = "Autocorrelação"
    ) +
    scale_x_continuous(breaks = seq(0, 104, by = 13))

p_acf
Figura 5: Função de Autocorrelação (ACF) da série de incidência
Interpretação do ACF
  • Decaimento lento: Indica não-estacionariedade (tendência ou raiz unitária)
  • Pico no lag 52: Forte correlação com a mesma semana do ano anterior (sazonalidade anual)
  • Autocorrelações significativas: Estrutura de dependência temporal que pode ser modelada

4.4.2 PACF - Função de Autocorrelação Parcial

O PACF mede a correlação entre \(Y_t\) e \(Y_{t-k}\) após remover o efeito dos lags intermediários.

Mostrar código
pacf_result <- pacf(y_clean, lag.max = 104, plot = FALSE)

pacf_df <- data.frame(
    lag = pacf_result$lag,
    pacf = pacf_result$acf
)

ggplot(pacf_df, aes(x = lag, y = pacf)) +
    geom_hline(yintercept = 0, color = "gray50") +
    geom_hline(yintercept = c(-ci, ci), linetype = "dashed", color = "blue") +
    geom_segment(aes(xend = lag, yend = 0), color = cores["observado"]) +
    geom_point(color = cores["observado"], size = 1) +
    labs(
        title = "Função de Autocorrelação Parcial (PACF)",
        subtitle = "Linhas azuis: intervalo de confiança 95%",
        x = "Lag (semanas)",
        y = "Autocorrelação Parcial"
    ) +
    scale_x_continuous(breaks = seq(0, 104, by = 13))
Figura 6: Função de Autocorrelação Parcial (PACF)
Sugestão de Modelo SARIMA

Baseado no ACF/PACF: - Componente AR: PACF com corte abrupto após lag 2 sugere \(p = 2\) - Diferenciação: Série precisa de 1 diferenciação sazonal (\(D = 1\)) - Modelo sugerido: SARIMA\((2, 0, 0) \times (0, 1, 0)_{52}\) ou similar

4.4.3 ACF da Série Diferenciada

Mostrar código
par(mfrow = c(1, 2))
acf(y_diff, lag.max = 104, main = "ACF - Série Diferenciada")
pacf(y_diff, lag.max = 104, main = "PACF - Série Diferenciada")
par(mfrow = c(1, 1))
Figura 7: ACF e PACF após diferenciação sazonal (lag 52)

4.5 Análise de Variáveis Climáticas

4.5.1 Correlação Cruzada (CCF)

A correlação cruzada identifica se e quando as variáveis climáticas precedem a incidência de dengue.

Mostrar código
# Filtrar dados completos
df_ccf <- df |> filter(!is.na(mean_temp) & !is.na(est_inc100k))

# CCF
ccf_temp <- ccf(df_ccf$mean_temp, df_ccf$est_inc100k, lag.max = 20, plot = FALSE)

ccf_df <- data.frame(
    lag = ccf_temp$lag,
    ccf = ccf_temp$acf
)

ci_ccf <- qnorm(0.975) / sqrt(nrow(df_ccf))

ggplot(ccf_df, aes(x = lag, y = ccf)) +
    geom_hline(yintercept = 0) +
    geom_hline(yintercept = c(-ci_ccf, ci_ccf), linetype = "dashed", color = "blue") +
    geom_col(fill = cores["temperatura"], width = 0.5) +
    geom_vline(xintercept = c(-8, -4), linetype = "dotted", color = "red") +
    labs(
        title = "Correlação Cruzada: Temperatura × Incidência",
        subtitle = "Lags negativos: temperatura PRECEDE incidência | Linhas vermelhas: lags ótimos",
        x = "Lag (semanas)",
        y = "Correlação"
    )
Figura 8: Correlação cruzada: Temperatura média × Incidência de dengue
Ciclo Biológico do Vetor

A correlação máxima em lags de 4-8 semanas é consistente com o ciclo biológico do Aedes aegypti:

  1. Temperatura elevada → ambiente favorável
  2. 1-2 semanas: Oviposição e desenvolvimento larval
  3. 2-4 semanas: Emergência de adultos
  4. 4-8 semanas: Transmissão e notificação de casos
Mostrar código
# CCF precipitacao
ccf_precip <- ccf(df_ccf$mean_precip, df_ccf$est_inc100k, lag.max = 20, plot = FALSE)

ccf_precip_df <- data.frame(
    lag = ccf_precip$lag,
    ccf = ccf_precip$acf
)

ggplot(ccf_precip_df, aes(x = lag, y = ccf)) +
    geom_hline(yintercept = 0) +
    geom_hline(yintercept = c(-ci_ccf, ci_ccf), linetype = "dashed", color = "blue") +
    geom_col(fill = cores["precipitacao"], width = 0.5) +
    labs(
        title = "Correlação Cruzada: Precipitação × Incidência",
        subtitle = "Lags negativos: precipitação PRECEDE incidência",
        x = "Lag (semanas)",
        y = "Correlação"
    )
Figura 9: Correlação cruzada: Precipitação × Incidência de dengue

5 Engenharia de Atributos (Feature Engineering)

Esta seção demonstra os módulos de engenharia de atributos disponíveis em R/features/ e explica a justificativa estatística para cada tipo de feature.

5.1 Visão Geral do Motor de Features

O módulo R/features/engine.R orquestra a criação de todas as features:

Mostrar código
# Carregar configuracao de features
config_features <- load_config("features")

# Listar tipos de features habilitadas
cat("=== Tipos de Features Habilitadas ===\n\n")
#> === Tipos de Features Habilitadas ===
Mostrar código
cat("Temporais:", config_features$temporal$enabled, "\n")
#> Temporais: TRUE
Mostrar código
cat("Sazonais:", config_features$seasonal$enabled, "\n")
#> Sazonais: TRUE
Mostrar código
cat("Climáticas:", config_features$climate$enabled, "\n")
#> Climáticas: TRUE

5.1.1 Função make_features()

Esta é a função principal que aplica todas as transformações:

Mostrar código
# Aplicar engenharia de features
df_features <- make_features(df, config_features)
#> [2026-01-11 14:04:35] INFO: ========== INICIO: Feature Engineering ========== 
#> [2026-01-11 14:04:35] INFO: Adicionando features temporais... 
#> [2026-01-11 14:04:36] INFO: Adicionando features de sazonalidade... 
#> [2026-01-11 14:04:36] INFO: Adicionando features climaticas... 
#> [2026-01-11 14:04:36] INFO: Features criadas: 47 novas colunas 
#> [2026-01-11 14:04:36] INFO: ========== FIM: Feature Engineering ==========
Mostrar código
# Listar novas features criadas
features_criadas <- get_feature_names(df_features)
cat("Total de features criadas:", length(features_criadas), "\n\n")
#> Total de features criadas: 47
Mostrar código
cat("Novas colunas:\n")
#> Novas colunas:
Mostrar código
print(features_criadas)
#>  [1] "lag_1"              "lag_2"              "lag_3"             
#>  [4] "lag_4"              "lag_6"              "lag_8"             
#>  [7] "lag_12"             "lag_26"             "lag_52"            
#> [10] "roll_mean_4"        "roll_mean_8"        "roll_mean_12"      
#> [13] "roll_max_4"         "roll_max_8"         "diff_1"            
#> [16] "diff_4"             "growth_4"           "sin_1"             
#> [19] "cos_1"              "sin_2"              "cos_2"             
#> [22] "temperature_lag2"   "temperature_lag4"   "temperature_lag6"  
#> [25] "temperature_lag8"   "temp_max_lag2"      "temp_max_lag4"     
#> [28] "temp_max_lag6"      "temp_max_lag8"      "temp_min_lag2"     
#> [31] "temp_min_lag4"      "temp_min_lag6"      "temp_min_lag8"     
#> [34] "precipitation_lag2" "precipitation_lag4" "precipitation_lag6"
#> [37] "precipitation_lag8" "temp_roll_mean_4"   "temp_roll_mean_8"  
#> [40] "precip_roll_sum_4"  "precip_roll_sum_8"  "precip_roll_sum_12"
#> [43] "temp_range"         "temp_range_lag2"    "temp_range_lag4"   
#> [46] "temp_range_lag6"    "temp_range_lag8"

5.2 Módulo R/features/temporal.R

5.2.1 Features de Lag

Os lags capturam a dependência temporal da série:

Mostrar código
data.frame(
    Funcao = rep("create_lag()", 3),
    Feature = c("lag_1 a lag_4", "lag_6, lag_8, lag_12", "lag_26, lag_52"),
    Tipo = c("Curto prazo", "Médio prazo", "Longo prazo"),
    Justificativa = c(
        "Memória recente: inércia epidêmica, casos geram novos casos",
        "Dinâmica intermediária: ciclo de transmissão completo",
        "Sazonalidade: correlação com semestre e ano anterior"
    )
) |>
    kable() |>
    kable_styling(bootstrap_options = c("striped", "hover"))
Tabela 4: Features de lag e suas justificativas
Funcao Feature Tipo Justificativa
create_lag() lag_1 a lag_4 Curto prazo Memória recente: inércia epidêmica, casos geram novos casos
create_lag() lag_6, lag_8, lag_12 Médio prazo Dinâmica intermediária: ciclo de transmissão completo
create_lag() lag_26, lag_52 Longo prazo Sazonalidade: correlação com semestre e ano anterior
Mostrar código
df_lags <- df_features |>
    select(data_iniSE, est_inc100k, lag_1, lag_4, lag_52) |>
    filter(!is.na(lag_52)) |>
    pivot_longer(cols = c(lag_1, lag_4, lag_52), names_to = "lag", values_to = "valor")

ggplot() +
    geom_line(
        data = df_features |> filter(!is.na(lag_52)),
        aes(x = data_iniSE, y = est_inc100k),
        color = cores["observado"],
        linewidth = 1.2,
        alpha = 0.8
    ) +
    geom_line(
        data = df_lags,
        aes(x = data_iniSE, y = valor, color = lag),
        linewidth = 0.8,
        alpha = 0.7
    ) +
    scale_color_brewer(
        palette = "Set1",
        labels = c("Lag 1 sem", "Lag 4 sem", "Lag 52 sem")
    ) +
    labs(
        title = "Série Original vs. Lags Temporais",
        subtitle = "Linha preta = Original | Coloridas = Lags deslocados",
        x = "Data",
        y = "Incidência",
        color = "Lag"
    )
Figura 10: Comparação de diferentes lags com a série original

5.2.2 Médias Móveis (Rolling Mean)

As médias móveis suavizam a série e capturam tendências locais:

\[\text{roll\_mean}_k(t) = \frac{1}{k} \sum_{i=1}^{k} Y_{t-i}\]

Mostrar código
df_roll <- df_features |>
    select(data_iniSE, est_inc100k, roll_mean_4, roll_mean_8, roll_mean_12) |>
    filter(!is.na(roll_mean_12))

ggplot(df_roll, aes(x = data_iniSE)) +
    geom_line(aes(y = est_inc100k),
        color = cores["observado"],
        linewidth = 0.5, alpha = 0.5
    ) +
    geom_line(aes(y = roll_mean_4, color = "4 semanas"), linewidth = 1) +
    geom_line(aes(y = roll_mean_8, color = "8 semanas"), linewidth = 1) +
    geom_line(aes(y = roll_mean_12, color = "12 semanas"), linewidth = 1) +
    scale_color_brewer(palette = "Set2") +
    labs(
        title = "Médias Móveis Suavizam a Série",
        subtitle = "Janelas maiores capturam tendências mais longas",
        x = "Data",
        y = "Incidência",
        color = "Janela"
    )
Figura 11: Médias móveis de diferentes janelas
Controle de Vazamento Temporal

A função rolling_mean() aplica lag de 1 automaticamente para evitar vazamento de informação futura. Isso é essencial para previsão!

# Implementação que evita vazamento:
c(NA_real_, result[1:(n - 1)])  # Desloca resultado 1 posição

5.2.3 Diferenças e Taxa de Crescimento

Mostrar código
data.frame(
    Funcao = c("create_diff()", "create_growth_rate()"),
    Feature = c("diff_1, diff_4", "growth_4"),
    Formula = c(
        "Y_t - Y_{t-k}",
        "(Y_t - Y_{t-k}) / (Y_{t-k} + ε)"
    ),
    Justificativa = c(
        "Taxa de variação absoluta: aceleração ou desaceleração",
        "Crescimento relativo: importante quando níveis variam muito"
    )
) |>
    kable() |>
    kable_styling(bootstrap_options = c("striped", "hover"))
Tabela 5: Features de diferenças e crescimento
Funcao Feature Formula Justificativa
create_diff() diff_1, diff_4 Y_t - Y_{t-k} Taxa de variação absoluta: aceleração ou desaceleração
create_growth_rate() growth_4 (Y_t - Y_{t-k}) / (Y_{t-k} + ε) Crescimento relativo: importante quando níveis variam muito

5.3 Módulo R/features/seasonal.R

5.3.1 Harmônicos de Fourier

Os harmônicos de Fourier representam a sazonalidade de forma contínua e suave:

\[\sin_k(t) = \sin\left(\frac{2\pi k \cdot \text{semana}}{52}\right)\] \[\cos_k(t) = \cos\left(\frac{2\pi k \cdot \text{semana}}{52}\right)\]

Mostrar código
data.frame(
    Ordem = c(1, 1, 2, 2),
    Componente = c("sin_1", "cos_1", "sin_2", "cos_2"),
    Periodo = c("52 semanas", "52 semanas", "26 semanas", "26 semanas"),
    Captura = c(
        "Fase da sazonalidade anual",
        "Amplitude da sazonalidade anual",
        "Fase da sazonalidade semestral",
        "Amplitude da sazonalidade semestral"
    )
) |>
    kable() |>
    kable_styling(bootstrap_options = c("striped", "hover"))
Tabela 6: Harmônicos de Fourier para sazonalidade
Ordem Componente Periodo Captura
1 sin_1 52 semanas Fase da sazonalidade anual
1 cos_1 52 semanas Amplitude da sazonalidade anual
2 sin_2 26 semanas Fase da sazonalidade semestral
2 cos_2 26 semanas Amplitude da sazonalidade semestral
Mostrar código
df_harm <- df_features |>
    select(semana, sin_1, cos_1, sin_2, cos_2) |>
    distinct() |>
    pivot_longer(cols = -semana, names_to = "harmonico", values_to = "valor")

ggplot(df_harm, aes(x = semana, y = valor, color = harmonico)) +
    geom_line(linewidth = 1.2) +
    scale_color_brewer(
        palette = "Set2",
        labels = c("cos(1)", "cos(2)", "sin(1)", "sin(2)")
    ) +
    labs(
        title = "Harmônicos de Fourier",
        subtitle = "Ordem 1 = Anual (52 sem) | Ordem 2 = Semestral (26 sem)",
        x = "Semana epidemiológica",
        y = "Valor",
        color = "Harmônico"
    )
Figura 12: Harmônicos de Fourier por semana epidemiológica
Vantagem dos Harmônicos

Comparado a dummies de semana (52 variáveis): - Menos parâmetros: 4 vs. 51 - Continuidade: Transição suave entre semanas - Regularização natural: Restrição implícita na forma da curva

5.3.2 Estatísticas Sazonais

Mostrar código
# Calcular media sazonal
seasonal_stats <- compute_seasonal_mean(df, target = "est_inc100k")

# Calcular quantis sazonais
seasonal_quantiles <- compute_seasonal_quantiles(df, target = "est_inc100k")

# Juntar dados
seasonal_profile <- left_join(seasonal_stats, seasonal_quantiles, by = "semana")

ggplot(seasonal_profile, aes(x = semana)) +
    geom_ribbon(aes(ymin = q25, ymax = q75), fill = cores["sazonalidade"], alpha = 0.3) +
    geom_ribbon(aes(ymin = q50 - seasonal_sd, ymax = q50 + seasonal_sd),
        fill = cores["sazonalidade"], alpha = 0.2
    ) +
    geom_line(aes(y = seasonal_mean), color = cores["observado"], linewidth = 1.2) +
    geom_line(aes(y = seasonal_median),
        color = cores["tendencia"],
        linewidth = 1, linetype = "dashed"
    ) +
    labs(
        title = "Perfil Sazonal da Incidência de Dengue",
        subtitle = "Linha preta = Média | Linha vermelha = Mediana | Faixa = IQR",
        x = "Semana epidemiológica",
        y = "Incidência por 100 mil"
    ) +
    scale_x_continuous(breaks = seq(1, 52, by = 4))
Figura 13: Perfil sazonal da incidência de dengue

5.4 Módulo R/features/climate.R

5.4.1 Lags Climáticos

Baseado na análise de CCF, criamos lags das variáveis climáticas:

Mostrar código
# Buscar config de clima
clima_config <- config_features$climate

data.frame(
    Variavel = c("Temperatura", "Temp. Máxima", "Temp. Mínima", "Precipitação"),
    Lags = paste(clima_config$lags, collapse = ", "),
    Justificativa = c(
        "Ciclo biológico do vetor: 2-8 semanas de atraso",
        "Mesma justificativa + extremos de calor",
        "Mesma justificativa + limites de desenvolvimento",
        "Acúmulo de criadouros: efeito mais lento"
    )
) |>
    kable() |>
    kable_styling(bootstrap_options = c("striped", "hover"))
Tabela 7: Features climáticas com defasagem
Variavel Lags Justificativa
Temperatura 2, 4, 6, 8 Ciclo biológico do vetor: 2-8 semanas de atraso
Temp. Máxima 2, 4, 6, 8 Mesma justificativa + extremos de calor
Temp. Mínima 2, 4, 6, 8 Mesma justificativa + limites de desenvolvimento
Precipitação 2, 4, 6, 8 Acúmulo de criadouros: efeito mais lento

5.4.2 Agregações Climáticas

Mostrar código
data.frame(
    Feature = c(
        "temp_roll_mean_4", "temp_roll_mean_8",
        "precip_roll_sum_4", "precip_roll_sum_8", "precip_roll_sum_12"
    ),
    Tipo = c(
        "Média móvel", "Média móvel",
        "Soma acumulada", "Soma acumulada", "Soma acumulada"
    ),
    Justificativa = c(
        "Condição térmica recente (1 mês)",
        "Condição térmica média (2 meses)",
        "Chuva acumulada recente",
        "Chuva acumulada médio prazo",
        "Chuva acumulada longo prazo (3 meses)"
    )
) |>
    kable() |>
    kable_styling(bootstrap_options = c("striped", "hover"))
Tabela 8: Agregações climáticas (rolling)
Feature Tipo Justificativa
temp_roll_mean_4 Média móvel Condição térmica recente (1 mês)
temp_roll_mean_8 Média móvel Condição térmica média (2 meses)
precip_roll_sum_4 Soma acumulada Chuva acumulada recente
precip_roll_sum_8 Soma acumulada Chuva acumulada médio prazo
precip_roll_sum_12 Soma acumulada Chuva acumulada longo prazo (3 meses)

5.4.3 Amplitude Térmica

A amplitude térmica (diferença entre máxima e mínima) é um indicador importante:

Mostrar código
if ("temp_range" %in% names(df_features)) {
    df_temp_range <- df_features |>
        filter(!is.na(temp_range)) |>
        select(data_iniSE, temp_range, est_inc100k)

    p1 <- ggplot(df_temp_range, aes(x = data_iniSE)) +
        geom_line(aes(y = temp_range), color = cores["temperatura"]) +
        labs(title = "Amplitude Térmica", y = "°C", x = NULL)

    p2 <- ggplot(df_temp_range, aes(x = temp_range, y = est_inc100k)) +
        geom_point(alpha = 0.3, color = cores["observado"]) +
        geom_smooth(method = "loess", color = cores["tendencia"]) +
        labs(title = "Relação com Incidência", x = "Amplitude (°C)", y = "Incidência")

    p1 + p2 + plot_annotation(title = "Feature: Amplitude Térmica (temp_max - temp_min)")
} else {
    cat("Feature temp_range não disponível nos dados processados.")
}
Figura 14: Amplitude térmica e sua relação com incidência

6 Análise de Correlações

6.1 Matriz de Correlação das Features

Mostrar código
# Selecionar features numericas
feature_names <- get_feature_names(df_features)
feature_names <- c("est_inc100k", feature_names)

df_corr <- df_features |>
    select(all_of(intersect(feature_names, names(df_features)))) |>
    select(where(is.numeric)) |>
    drop_na()

# Calcular matriz de correlacao
cor_matrix <- cor(df_corr, use = "pairwise.complete.obs")

# Plot
corrplot(
    cor_matrix,
    method = "color",
    type = "lower",
    order = "hclust",
    tl.cex = 0.6,
    tl.col = "black",
    addCoef.col = "gray30",
    number.cex = 0.5,
    diag = FALSE,
    title = "Correlação entre Features",
    mar = c(0, 0, 2, 0)
)
Figura 15: Matriz de correlação das features criadas

6.2 Correlação com o Alvo

Mostrar código
# Calcular correlacoes com o alvo
cor_with_target <- cor_matrix[, "est_inc100k"]
cor_with_target <- cor_with_target[names(cor_with_target) != "est_inc100k"]

# Top 15
top_cors <- sort(abs(cor_with_target), decreasing = TRUE)[1:15]

cor_df <- data.frame(
    feature = names(top_cors),
    correlacao = cor_with_target[names(top_cors)]
) |>
    mutate(
        tipo = case_when(
            grepl("lag_|roll_|diff_|growth", feature) ~ "Temporal",
            grepl("sin_|cos_", feature) ~ "Sazonal",
            grepl("temp|precip", feature) ~ "Climática",
            TRUE ~ "Outra"
        )
    )

ggplot(cor_df, aes(x = reorder(feature, abs(correlacao)), y = correlacao, fill = tipo)) +
    geom_col() +
    coord_flip() +
    scale_fill_brewer(palette = "Set2") +
    geom_hline(yintercept = 0, linetype = "dashed") +
    labs(
        title = "Top 15 Features Mais Correlacionadas com Incidência",
        x = NULL,
        y = "Correlação de Pearson",
        fill = "Tipo"
    )
Figura 16: Top 15 features mais correlacionadas com a incidência

6.3 Multicolinearidade

Mostrar código
# Encontrar pares com alta correlacao
high_cor <- which(abs(cor_matrix) > 0.9 & abs(cor_matrix) < 1, arr.ind = TRUE)

if (nrow(high_cor) > 0) {
    high_cor_pairs <- data.frame(
        Feature1 = rownames(cor_matrix)[high_cor[, 1]],
        Feature2 = colnames(cor_matrix)[high_cor[, 2]],
        Correlacao = round(cor_matrix[high_cor], 3)
    ) |>
        filter(Feature1 < Feature2) |> # Remover duplicatas
        arrange(desc(abs(Correlacao))) |>
        head(10)

    high_cor_pairs |>
        kable() |>
        kable_styling(bootstrap_options = c("striped", "hover"))
} else {
    cat("Nenhum par de features com correlação > 0.9")
}
Tabela 9: Pares de features com alta correlação (> 0.9)
Feature1 Feature2 Correlacao
lag_2 roll_mean_4 0.996
lag_3 roll_mean_4 0.996
roll_max_4 roll_mean_4 0.992
lag_4 roll_mean_8 0.992
lag_3 roll_max_4 0.989
est_inc100k lag_1 0.986
lag_1 lag_2 0.986
lag_2 lag_3 0.986
lag_3 lag_4 0.986
lag_6 roll_mean_12 0.986
Atenção: Multicolinearidade

Features altamente correlacionadas podem causar: - Instabilidade em coeficientes de modelos lineares - Redundância que aumenta dimensionalidade desnecessariamente - Overfitting em modelos de árvore

Recomendação: Considerar seleção de features ou regularização (Lasso, Ridge).


7 Conclusões e Próximos Passos

7.1 Principais Achados

Mostrar código
data.frame(
    Aspecto = c(
        "Qualidade dos Dados",
        "Sazonalidade",
        "Estacionariedade",
        "Autocorrelação",
        "Clima",
        "Features"
    ),
    Achado = c(
        dq_report$passed |> ifelse("Dados de boa qualidade", "Há problemas a tratar"),
        "Forte padrão anual com picos no verão (52 semanas)",
        "Série não-estacionária; diferenciação sazonal necessária",
        "Dependência de curto prazo (AR) e sazonalidade (lag 52)",
        "Temperatura precede incidência em 4-8 semanas (CCF)",
        sprintf(
            "%d features criadas cobrindo aspectos temporais, sazonais e climáticos",
            length(features_criadas)
        )
    ),
    Implicacao = c(
        "Pode-se prosseguir com modelagem",
        "Modelos devem incluir componente sazonal (SARIMA, Prophet)",
        "SARIMA com D=1 ou diferenciação antes de ML",
        "Modelos AR são apropriados; considerar p=2",
        "Incluir lags climáticos nos modelos de regressão",
        "Seleção de features pode melhorar performance"
    )
) |>
    kable() |>
    kable_styling(bootstrap_options = c("striped", "hover"))
Tabela 10: Resumo dos principais achados da análise exploratória
Aspecto Achado Implicacao
Qualidade dos Dados Dados de boa qualidade Pode-se prosseguir com modelagem
Sazonalidade Forte padrão anual com picos no verão (52 semanas) Modelos devem incluir componente sazonal (SARIMA, Prophet)
Estacionariedade Série não-estacionária; diferenciação sazonal necessária SARIMA com D=1 ou diferenciação antes de ML
Autocorrelação Dependência de curto prazo (AR) e sazonalidade (lag 52) Modelos AR são apropriados; considerar p=2
Clima Temperatura precede incidência em 4-8 semanas (CCF) Incluir lags climáticos nos modelos de regressão
Features 47 features criadas cobrindo aspectos temporais, sazonais e climáticos Seleção de features pode melhorar performance

7.2 Recomendações para Modelagem

Modelos Recomendados
  1. SARIMA\((2,0,0) \times (0,1,0)_{52}\): Modelo baseline estatístico
  2. Prophet: Para captura automática de tendência e sazonalidade
  3. XGBoost/LightGBM: Com features temporais, sazonais e climáticas
  4. Ensemble: Combinar modelos para robustez

7.3 Limitações

  • Mudança de regime em 2024: Surto sem precedentes pode afetar modelos treinados em dados históricos
  • Covariáveis limitadas: Apenas clima; fatores socioeconômicos e ações de controle não incluídos
  • Horizonte de previsão: Qualidade degrada significativamente após 4-6 semanas

7.4 Próximos Passos

  1. Treinar modelos com diferentes horizontes (h = 4, 6, 8 semanas)
  2. Validação temporal via rolling-origin (já implementada em preprocess.R)
  3. Comparar métricas (MAE, RMSE, MASE) entre modelos
  4. Avaliar calibração das previsões probabilísticas
  5. Investigar mudança de regime e possível adaptação de modelos

8 Referências dos Módulos R

Mostrar código
data.frame(
    Modulo = c(
        "R/data/loader.R",
        "R/data/preprocess.R",
        "R/data/quality.R",
        "R/features/temporal.R",
        "R/features/seasonal.R",
        "R/features/climate.R",
        "R/features/engine.R"
    ),
    Funcoes = c(
        "load_raw_data(), aggregate_state(), load_processed_data()",
        "preprocess_data(), impute_climate_nas(), create_rolling_splits()",
        "run_data_quality(), check_completeness(), check_duplicates()",
        "add_temporal_features(), create_lag(), rolling_mean(), create_diff()",
        "add_seasonal_features(), create_fourier_terms(), compute_seasonal_mean()",
        "add_climate_features(), add_climate_anomalies(), create_extreme_indicator()",
        "make_features(), clean_features(), create_model_matrix()"
    ),
    Proposito = c(
        "Carregamento e agregação de dados",
        "Preprocessamento e validação temporal",
        "Controle de qualidade",
        "Features baseadas em lags e agregações",
        "Harmônicos de Fourier e estatísticas sazonais",
        "Lags e transformações de variáveis climáticas",
        "Orquestração e limpeza de features"
    )
) |>
    kable() |>
    kable_styling(bootstrap_options = c("striped", "hover"))
Tabela 11: Resumo dos módulos R utilizados neste relatório
Modulo Funcoes Proposito
R/data/loader.R load_raw_data(), aggregate_state(), load_processed_data() Carregamento e agregação de dados
R/data/preprocess.R preprocess_data(), impute_climate_nas(), create_rolling_splits() Preprocessamento e validação temporal
R/data/quality.R run_data_quality(), check_completeness(), check_duplicates() Controle de qualidade
R/features/temporal.R add_temporal_features(), create_lag(), rolling_mean(), create_diff() Features baseadas em lags e agregações
R/features/seasonal.R add_seasonal_features(), create_fourier_terms(), compute_seasonal_mean() Harmônicos de Fourier e estatísticas sazonais
R/features/climate.R add_climate_features(), add_climate_anomalies(), create_extreme_indicator() Lags e transformações de variáveis climáticas
R/features/engine.R make_features(), clean_features(), create_model_matrix() Orquestração e limpeza de features

9 Informações da Sessão

Mostrar código
sessionInfo()
#> R version 4.5.1 (2025-06-13)
#> Platform: aarch64-apple-darwin20
#> Running under: macOS Sequoia 15.7.3
#> 
#> Matrix products: default
#> BLAS:   /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRblas.0.dylib 
#> LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.1
#> 
#> locale:
#> [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
#> 
#> time zone: America/Sao_Paulo
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#>  [1] corrplot_0.95    plotly_4.11.0    kableExtra_1.4.0 knitr_1.51      
#>  [5] scales_1.4.0     patchwork_1.3.2  tseries_0.10-58  forecast_8.24.0 
#>  [9] lubridate_1.9.4  forcats_1.0.1    stringr_1.6.0    dplyr_1.1.4     
#> [13] purrr_1.2.0      readr_2.1.5      tidyr_1.3.1      tibble_3.3.0    
#> [17] ggplot2_4.0.0    tidyverse_2.0.0 
#> 
#> loaded via a namespace (and not attached):
#>  [1] gtable_0.3.6          xfun_0.53             htmlwidgets_1.6.4    
#>  [4] lattice_0.22-7        tzdb_0.5.0            crosstalk_1.2.2      
#>  [7] quadprog_1.5-8        vctrs_0.6.5           tools_4.5.1          
#> [10] generics_0.1.4        curl_7.0.0            parallel_4.5.1       
#> [13] xts_0.14.1            pkgconfig_2.0.3       Matrix_1.7-3         
#> [16] prophet_1.0           data.table_1.17.8     RColorBrewer_1.1-3   
#> [19] S7_0.2.0              RcppParallel_5.1.11-1 lifecycle_1.0.4      
#> [22] compiler_4.5.1        farver_2.1.2          textshaping_1.0.3    
#> [25] codetools_0.2-20      htmltools_0.5.8.1     lazyeval_0.2.2       
#> [28] yaml_2.3.12           pillar_1.11.1         nlme_3.1-168         
#> [31] fracdiff_1.5-3        tidyselect_1.2.1      digest_0.6.37        
#> [34] stringi_1.8.7         splines_4.5.1         labeling_0.4.3       
#> [37] rprojroot_2.1.1       fastmap_1.2.0         grid_4.5.1           
#> [40] here_1.0.2            colorspace_2.1-2      cli_3.6.5            
#> [43] magrittr_2.0.4        withr_3.0.2           xgboost_3.1.2.1      
#> [46] timechange_0.3.0      httr_1.4.7            TTR_0.24.4           
#> [49] rmarkdown_2.30        quantmod_0.4.28       nnet_7.3-20          
#> [52] timeDate_4051.111     zoo_1.8-14            hms_1.1.4            
#> [55] urca_1.3-4            evaluate_1.0.5        lmtest_0.9-40        
#> [58] viridisLite_0.4.2     mgcv_1.9-4            rlang_1.1.6          
#> [61] Rcpp_1.1.0            glue_1.8.0            xml2_1.4.1           
#> [64] svglite_2.2.2         rstudioapi_0.17.1     jsonlite_2.0.0       
#> [67] R6_2.6.1              systemfonts_1.3.1