Nucleo de Inteligencia e Vigilancia em Saude (NIVS) - SES/SP
Data de Publicação
14 de outubro de 2025
1 Introdução
Este relatório apresenta, de forma objetiva, (i) as fontes de dados utilizadas para SRAG em SP entre 2009–2025 e (ii) o processo de harmonização da classificação final (o “Roseta”), que produz a coluna padronizada CLASSI_FIN_UNI (e registra a origem em CLASSI_FONTE). Abaixo estão os escopos por período e as regras de harmonização.
1.1 Escopo e objetivos
Objetivo analítico: consolidar séries e indicadores de SRAG (casos/óbitos) para SP.
Padronização: unificar classificações heterogêneas em CLASSI_FIN_UNI.
Rastreabilidade: documentar a origem da decisão em CLASSI_FONTE.
Nota
Por que “Roseta”? Assim como a Pedra de Roseta permitiu decifrar sistemas distintos, aqui harmonizamos classificações e estruturas diferentes entre 2009–2012, 2013–2019 e 2019–2025.
1.2 Fontes de dados — 2009–2019
Sistemas:
SINAN Influenza WEB — Ficha v8 (rev. mar/2013): SRAG internado/óbito.
SIVEP Gripe — Ficha de registro individual (V.02, 07/05/2019): SRAG hospitalizado.
Origem operacional:vw_sivep_com_drs (consulta institucional).
Campos utilizados: DT_SIN_PRI
classi_resumida
COD_6_mun
obito
POP2023
POP2024
Tratamentos principais:
Conversão de datas (DT_SIN_PRI) e ano (ano).
ID_MUNICIPIO derivado de COD_6_mun.
Normalização de classi_resumida → CLASSI_FIN_UNI.
OBITO como binário (0/1).
Populações auxiliares (POP2023, POP2024) para taxas/ajustes.
Mapeamento de classi_resumida → CLASSI_FIN_UNI (ASCII): - influenza → SRAG por Influenza
- virus sincicial respiratorio → SRAG por VSR
- covid-19 → SRAG por covid-19
- outras etiologias → SRAG por outros agentes etiologicos
- srag nao especificado / srag em investigacao → SRAG nao especificado (Outros valores não mapeados caem em “SRAG nao especificado”.)
1.4 Categorias finais padronizadas (CLASSI_FIN_UNI)
SRAG por Influenza
SRAG por VSR
SRAG por covid-19(apenas 2019–2025)
SRAG por outros vírus respiratórios
SRAG por outros agentes etiologicos
SRAG nao especificado
Descartado(compatibilização 2009–2012)
Ignorado/Indefinido
1.5 Harmonização “Roseta” — 2009–2019 (cascata de decisão)
Princípios gerais (hierarquia de evidências):
Laboratório > texto de encerramento (CLASSI_FIN).
RT-PCR > IFI (imunofluorescência).
Co-infecção (Influenza + outro vírus) → classificar como SRAG por Influenza.
Regras (aplicadas linha a linha):
Co-infecção (Influenza + outro vírus) → SRAG por Influenza. Evidências aceitas: PCR, IFI, RES_FLUA/RES_FLUB, RES_VSR.
RT-PCR positivo para Influenza (A/B) → SRAG por Influenza. Colunas típicas:POS_PCRFLU, PCR_FLUASU, PCR_FLUBLI.
RT-PCR positivo para outros vírus (VSR/Parainfluenza/Adeno/Metapneumo/Rino/Boca/“outro”)
→ SRAG por outros vírus respiratórios. Colunas típicas:PCR_VSR, PCR_PARA1–4, PCR_ADENO, PCR_METAP, PCR_RINO, PCR_BOCA, PCR_OUTRO.
IFI positivo para Influenza (sem RT-PCR conclusivo) → SRAG por Influenza. Colunas:POS_IF_FLU, TP_FLU_IF.
IFI positivo somente para outros vírus (sem RT-PCR conclusivo)
→ SRAG por outros vírus respiratórios. Colunas:IF_VSR, IF_PARA1–3, IF_ADENO, IF_OUTRO.
Outros agentes etiológicos (não respiratórios)
→ SRAG por outros agentes etiologicos. Apoio:RES_OUTRO, DS_OAGEETI, CLASSI_OUT.
Sem evidência laboratorial aplicável → usar CLASSI_FIN
Para 2019–2025, a coluna classi_resumida já agrega a lógica operacional do período. Aplicamos normalização textual e mapeamento 1→1 para CLASSI_FIN_UNI (incluindo covid-19 e VSR). Valores ambíguos/pendentes são tratados como SRAG nao especificado. Fatores são fixados na ordem canônica para garantir consistência de gráficos/tabelas.
1.7 Qualidade e inconsistências
Sinalizamos casos com incoerência entre CRITERIO e a harmonização (ex.: CRITERIO = "Laboratorial" sem agente definido) em FLAG_INCONSISTENCIA.
Monitoramos ausências de data, códigos municipais inválidos e categorias fora do dicionário.
1.7.1 Glossário mínimo de colunas
DT_SIN_PRI: data do início dos sintomas.
CLASSI_FIN: classificação final original de encerramento.
CLASSI_FIN_UNI: classificação final harmonizada (saída).
CLASSI_FONTE: trilha de auditoria da decisão (PCR/IFI/CLASSI_FIN/Residual).
CRITERIO: critério de confirmação do caso.
ID_MUNICIPIO: código do município de notificação.
OBITO: desfecho (0/1).
2 Série temporal semanal — casos por classificação
3 Composição percentual (área empilhada)
4 Série temporal semanal – óbitos
5 Composição percentual — óbitos semanais
6 Casos por ano e classificacao
Código fonte
---title: "SRAG – SP 2009–2025"author: "Nucleo de Inteligencia e Vigilancia em Saude (NIVS) - SES/SP"date: todaydate-format: "D [de] MMMM [de] YYYY"lang: pt-BRformat: html: embed-resources: true # HTML autocontido (essencial no RPubs) toc: true toc-depth: 3 number-sections: true smooth-scroll: true code-tools: true # botão para mostrar/ocultar código quando echo=FALSE df-print: paged code-fold: false theme: flatly title-block-banner: false link-external-newwindow: trueeditor: visualexecute: echo: false # esconder código por padrão (usável via code-tools) warning: false message: false cache: true # acelera re-render sem mudar dadosfreeze: auto # congela saída até o código mudar (bom p/ rel. estável)---## IntroduçãoEste relatório apresenta, de forma objetiva, (i) as **fontes de dados** utilizadas para SRAG em SP entre **2009–2025** e (ii) o **processo de harmonização** da classificação final (o **“Roseta”**), que produz a coluna padronizada `CLASSI_FIN_UNI` (e registra a origem em `CLASSI_FONTE`). Abaixo estão os escopos por período e as regras de harmonização.------------------------------------------------------------------------### Escopo e objetivos- **Objetivo analítico:** consolidar séries e indicadores de SRAG (casos/óbitos) para SP.- **Padronização:** unificar classificações heterogêneas em `CLASSI_FIN_UNI`.- **Rastreabilidade:** documentar a origem da decisão em `CLASSI_FONTE`.::: callout-note**Por que “Roseta”?** Assim como a Pedra de Roseta permitiu decifrar sistemas distintos, aqui harmonizamos **classificações e estruturas diferentes** entre 2009–2012, 2013–2019 e 2019–2025.:::------------------------------------------------------------------------### Fontes de dados — 2009–2019- **Sistemas:** - **SINAN Influenza WEB** — Ficha v8 (rev. mar/2013): SRAG internado/óbito.\ - **SIVEP Gripe** — Ficha de registro individual (V.02, 07/05/2019): SRAG hospitalizado.- **Abrangência:** Estado de São Paulo (**SP**).\- **Aquisição:** arquivos anuais `INFLUD09.csv` … `INFLUD19.csv`.\- **Filtro inicial:** `SG_UF_NOT ∈ {"35","SP"}`.\- **Colunas-base (mínimo):**\`DT_SIN_PRI` (início sintomas)`DT_DIGITA``ID_MUNICIPIO` (de `CO_MUN_NOT`/`ID_MUNICIP`)`CLASSI_FIN` (classificação final original)`CRITERIO``DT_ENCERRA`------------------------------------------------------------------------### Fontes de dados — 2019–2025 (SINAN)- **Origem operacional:** `vw_sivep_com_drs` (consulta institucional).\- **Campos utilizados:**\`DT_SIN_PRI`- `classi_resumida`- `COD_6_mun`- `obito`- `POP2023`- `POP2024`\- **Tratamentos principais:** - Conversão de datas (`DT_SIN_PRI`) e ano (`ano`).\ - `ID_MUNICIPIO` derivado de `COD_6_mun`.\ - Normalização de `classi_resumida` → `CLASSI_FIN_UNI`.\ - `OBITO` como binário (0/1).\ - Populações auxiliares (`POP2023`, `POP2024`) para taxas/ajustes.**Mapeamento de `classi_resumida` → `CLASSI_FIN_UNI` (ASCII):** - `influenza` → **SRAG por Influenza**\- `virus sincicial respiratorio` → **SRAG por VSR**\- `covid-19` → **SRAG por covid-19**\- `outras etiologias` → **SRAG por outros agentes etiologicos**\- `srag nao especificado` / `srag em investigacao` → **SRAG nao especificado**\*(Outros valores não mapeados caem em “SRAG nao especificado”.)*------------------------------------------------------------------------### Categorias finais padronizadas (`CLASSI_FIN_UNI`)- **SRAG por Influenza**- **SRAG por VSR**- **SRAG por covid-19** *(apenas 2019–2025)*- **SRAG por outros vírus respiratórios**- **SRAG por outros agentes etiologicos**- **SRAG nao especificado**- **Descartado** *(compatibilização 2009–2012)*- **Ignorado/Indefinido**------------------------------------------------------------------------### Harmonização “Roseta” — 2009–2019 (cascata de decisão)**Princípios gerais (hierarquia de evidências):**1. **Laboratório \> texto de encerramento** (`CLASSI_FIN`).2. **RT-PCR \> IFI** (imunofluorescência).3. **Co-infecção (Influenza + outro vírus)** → classificar como **SRAG por Influenza**.**Regras (aplicadas linha a linha):**1. **Co-infecção (Influenza + outro vírus)** → **SRAG por Influenza**.\ *Evidências aceitas:* PCR, IFI, `RES_FLUA`/`RES_FLUB`, `RES_VSR`.2. **RT-PCR positivo para Influenza (A/B)** → **SRAG por Influenza**.\ *Colunas típicas:* `POS_PCRFLU`, `PCR_FLUASU`, `PCR_FLUBLI`.3. **RT-PCR positivo para outros vírus** (VSR/Parainfluenza/Adeno/Metapneumo/Rino/Boca/“outro”)\ → **SRAG por outros vírus respiratórios**.\ *Colunas típicas:* `PCR_VSR`, `PCR_PARA1–4`, `PCR_ADENO`, `PCR_METAP`, `PCR_RINO`, `PCR_BOCA`, `PCR_OUTRO`.4. **IFI positivo para Influenza** (sem RT-PCR conclusivo) → **SRAG por Influenza**.\ *Colunas:* `POS_IF_FLU`, `TP_FLU_IF`.5. **IFI positivo somente para outros vírus** (sem RT-PCR conclusivo)\ → **SRAG por outros vírus respiratórios**.\ *Colunas:* `IF_VSR`, `IF_PARA1–3`, `IF_ADENO`, `IF_OUTRO`.6. **Outros agentes etiológicos (não respiratórios)**\ → **SRAG por outros agentes etiologicos**.\ *Apoio:* `RES_OUTRO`, `DS_OAGEETI`, `CLASSI_OUT`.7. **Sem evidência laboratorial aplicável → usar `CLASSI_FIN`**\ - **2009–2012:** 1 = Influenza; 2 = Outros agentes; 3 = Descartado.\ - **2013–2019:** 1 = Influenza; 2 = Outros vírus resp.; 3 = Outros agentes; 4 = Não especificado; 9 = Ignorado.8. **Residual** (ausência/ambiguidade persistente) → **SRAG nao especificado**.::: callout-important**Rastreabilidade:** a origem da decisão é registrada em `CLASSI_FONTE` (ex.: “PCR”, “IFI”, “CLASSI_FIN 2013–2019”, “residual”).:::------------------------------------------------------------------------### Harmonização — 2019–2025 (normalização direta)Para 2019–2025, a coluna `classi_resumida` já agrega a lógica operacional do período. Aplicamos **normalização textual** e mapeamento **1→1** para `CLASSI_FIN_UNI` (incluindo **covid-19** e **VSR**). Valores ambíguos/pendentes são tratados como **SRAG nao especificado**. Fatores são fixados na ordem canônica para garantir consistência de gráficos/tabelas.------------------------------------------------------------------------### Qualidade e inconsistências- Sinalizamos casos com **incoerência entre `CRITERIO` e a harmonização** (ex.: `CRITERIO = "Laboratorial"` sem agente definido) em **`FLAG_INCONSISTENCIA`**.\- Monitoramos **ausências de data**, códigos municipais inválidos e **categorias fora do dicionário**.::: details#### Glossário mínimo de colunas- `DT_SIN_PRI`: data do início dos sintomas.\- `CLASSI_FIN`: classificação final original de encerramento.\- `CLASSI_FIN_UNI`: classificação final **harmonizada** (saída).\- `CLASSI_FONTE`: **trilha de auditoria** da decisão (PCR/IFI/CLASSI_FIN/Residual).\- `CRITERIO`: critério de confirmação do caso.\- `ID_MUNICIPIO`: código do município de notificação.\- `OBITO`: desfecho (0/1).\:::```{r}#| label: setup#| include: false# Dependências mínimas (removidos pacotes não usados no render atual)suppressPackageStartupMessages({library(dplyr)library(tidyr)library(lubridate)library(ggplot2)library(plotly)library(forcats)library(stringr)library(scales)library(DT)})# Opções globais úteisoptions(scipen =999) # evita notação científica involuntáriatheme_set(theme_minimal(base_size =12))# Níveis padronizados (ordem usada em fatores/legendas)niv_ascii <-c("SRAG por Influenza","SRAG por VSR","SRAG por covid-19","SRAG por outros virus respiratorios","SRAG por outros agentes etiologicos","SRAG nao especificado","Descartado","Ignorado/Indefinido")# Funções auxiliares ----------------------------# Checagem defensiva de colunas obrigatóriasassert_cols <-function(df, cols) { miss <-setdiff(cols, names(df))if (length(miss)) {stop(sprintf("Colunas ausentes: %s", paste(miss, collapse =", ")), call. =FALSE) }}# Semana epidemiológica (BR): SE inicia no domingo e SE1 é a que contém mais dias de janeirostart_se1_br <-function(y) { from <-as.Date(sprintf("%d-12-28", y -1)) to <-as.Date(sprintf("%d-01-04", y)) cand <-seq.Date(from, to, by ="day") cand <- cand[lubridate::wday(cand, week_start =7) ==1] jan_days <-vapply( cand,function(s) { w <-seq.Date(s, s +6, by ="day")sum(lubridate::month(w) ==1& lubridate::year(w) == y) },integer(1) ) cand[which.max(jan_days)]}add_epiweek_br <-function(df, date_col ="DT_SIN_PRI") { d <- df[[date_col]]stopifnot(inherits(d, "Date")) y_min <- lubridate::year(min(d, na.rm =TRUE)) -1L y_max <- lubridate::year(max(d, na.rm =TRUE)) +1L anos <-seq.int(y_min, y_max, by =1L) se1_starts <-vapply(anos, start_se1_br, as.Date("1970-01-01")) ord <-order(se1_starts); anos <- anos[ord]; se1_starts <- se1_starts[ord] idx <-findInterval(d, se1_starts); idx[idx <1] <-NA_integer_ se_ano <-ifelse(is.na(idx), NA_integer_, anos[idx]) delta_days <-rep(NA_integer_, length(d)); ok <-!is.na(idx) delta_days[ok] <-as.integer(as.numeric(d[ok] - se1_starts[idx[ok]])) weeks_off <-rep(NA_integer_, length(d)); weeks_off[ok] <- delta_days[ok] %/%7L se_ini <-as.Date(NA); se_fim <-as.Date(NA); se_num <-rep(NA_integer_, length(d)) se_ini[ok] <- se1_starts[idx[ok]] + weeks_off[ok] *7L se_fim[ok] <- se_ini[ok] +6L se_num[ok] <- weeks_off[ok] +1L dplyr::mutate(df, SE_ANO = se_ano, SE = se_num, SE_INICIO = se_ini, SE_FIM = se_fim)}# Coalesce seguro para inteiroscoalesce_int <-function(x, val =0L) { x <-suppressWarnings(as.integer(x)) dplyr::coalesce(x, val)}``````{r}#| label: dados-carregar# Defina a pasta de dados (RDS)dir_out <-"dados_exportados"# Checagens de existência (falha amigável se não encontrar)if (!file.exists(file.path(dir_out, "df_sp_2009_2025se.rds"))) {stop("Arquivo 'df_sp_2009_2025se.rds' não encontrado em '", dir_out, "'.", call. =FALSE)}if (!file.exists(file.path(dir_out, "df_sp_2009_2025.rds"))) {stop("Arquivo 'df_sp_2009_2025.rds' não encontrado em '", dir_out, "'.", call. =FALSE)}# Leituradf_sp_2009_2025se <-readRDS(file.path(dir_out, "df_sp_2009_2025se.rds"))df_sp_2009_2025 <-readRDS(file.path(dir_out, "df_sp_2009_2025.rds"))# Harmonização mínima de tiposdf_sp_2009_2025 <- df_sp_2009_2025 %>%mutate(DT_SIN_PRI =as.Date(DT_SIN_PRI),CLASSI_FIN_UNI =factor(as.character(CLASSI_FIN_UNI), levels = niv_ascii),OBITO =coalesce_int(OBITO, 0L),ano = lubridate::year(DT_SIN_PRI) )# Garantias de colunas necessáriasassert_cols(df_sp_2009_2025se, c("SE_INICIO", "SE_ANO", "SE"))assert_cols(df_sp_2009_2025, c("DT_SIN_PRI", "CLASSI_FIN_UNI", "OBITO", "ano"))```## Série temporal semanal — casos por classificação```{r}#| label: ts-casos# Longa: uma linha por (semana x classe)ts_semana_casos <- df_sp_2009_2025se %>%select(SE_INICIO, SE_ANO, SE, starts_with("n_")) %>%pivot_longer(cols =starts_with("n_"),names_to ="classe", values_to ="n", values_drop_na =TRUE ) %>%mutate(classe =sub("^n_", "", classe),classe =factor(classe, levels = niv_ascii) ) %>%arrange(SE_INICIO, classe)# Gráfico (ggplotly com tooltips limpos)p_casos <-ggplot(ts_semana_casos, aes(x = SE_INICIO, y = n, color = classe)) +geom_line(linewidth =0.6) +labs(title ="SRAG – frequência semanal de casos por classificação",x ="Semana (início, domingo)",y ="Número de casos",color ="Classificação" ) +scale_x_date(date_breaks ="3 months", date_labels ="%Y-%m") +theme(axis.text.x =element_text(angle =45, hjust =1),legend.position ="bottom" )ggplotly(p_casos, tooltip =c("x", "y", "colour"))```## Composição percentual (área empilhada)```{r}#| label: ts-casos-sharets_share <- ts_semana_casos %>%group_by(SE_INICIO) %>%mutate(total =sum(n, na.rm =TRUE),perc =if_else(total >0, n / total, 0) ) %>%ungroup()p_share <-ggplot(ts_share, aes(x = SE_INICIO, y = perc, fill = classe)) +geom_area() +scale_y_continuous(labels = percent) +labs(title ="Composição semanal por classificação (percentual)",x ="Semana (início, domingo)",y ="% do total",fill ="Classificação" ) +scale_x_date(date_breaks ="6 months", date_labels ="%Y-%m") +theme(axis.text.x =element_text(angle =45, hjust =1),legend.position ="bottom" )ggplotly(p_share, tooltip =c("x", "y", "fill"))```## Série temporal semanal – óbitos```{r}#| label: ts-obitos# Preparação e agregação por semana/condiçãoobitos_semanais <- df_sp_2009_2025 %>%filter(!is.na(DT_SIN_PRI), !is.na(CLASSI_FIN_UNI)) %>%add_epiweek_br("DT_SIN_PRI") %>%group_by(SE_INICIO, CLASSI_FIN_UNI) %>%summarise(obitos =sum(OBITO ==1, na.rm =TRUE), .groups ="drop") %>%group_by(CLASSI_FIN_UNI) %>%complete(SE_INICIO =seq.Date(min(SE_INICIO), max(SE_INICIO), by ="week"),fill =list(obitos =0L) ) %>%ungroup() %>%arrange(SE_INICIO, CLASSI_FIN_UNI)p_obitos_doenca <-ggplot(obitos_semanais, aes(x = SE_INICIO, y = obitos, color = CLASSI_FIN_UNI)) +geom_line(linewidth =0.6) +labs(title ="SRAG – óbitos semanais por classificação (DT_SIN_PRI → Semana Epidemiológica)",x ="Semana (início, domingo)",y ="Número de óbitos",color ="Classificação" ) +scale_x_date(date_breaks ="3 months", date_labels ="%Y-%m") +theme(axis.text.x =element_text(angle =45, hjust =1),legend.position ="bottom" )ggplotly(p_obitos_doenca, tooltip =c("x", "y", "colour"))```## Composição percentual — óbitos semanais```{r}#| label: ts-obitos-sharets_share_obitos <- obitos_semanais %>%group_by(SE_INICIO) %>%mutate(total_semana =sum(obitos, na.rm =TRUE),perc =if_else(total_semana >0, obitos / total_semana, 0) ) %>%ungroup()p_share_obitos <-ggplot(ts_share_obitos, aes(x = SE_INICIO, y = perc, fill = CLASSI_FIN_UNI)) +geom_area() +scale_y_continuous(labels = scales::percent) +labs(title ="Composição semanal de óbitos por classificação (percentual)",x ="Semana (início, domingo)",y ="% do total de óbitos",fill ="Classificação" ) +scale_x_date(date_breaks ="6 months", date_labels ="%Y-%m") +theme(axis.text.x =element_text(angle =45, hjust =1),legend.position ="bottom" )ggplotly(p_share_obitos, tooltip =c("x", "y", "fill"))```## Casos por ano e classificacao```{r}#| label: tabela-ano-classetab_ano_classe <- df_sp_2009_2025 %>%mutate(CLASSI_FIN_UNI =as.character(CLASSI_FIN_UNI)) %>%mutate(CLASSI_FIN_UNI =if_else(CLASSI_FIN_UNI %in% niv_ascii, CLASSI_FIN_UNI, NA_character_)) %>%filter(!is.na(CLASSI_FIN_UNI)) %>%count(ano, CLASSI_FIN_UNI, name ="casos") %>%arrange(ano, CLASSI_FIN_UNI) %>%pivot_wider(names_from = CLASSI_FIN_UNI, values_from = casos, values_fill =0L)DT::datatable( tab_ano_classe,rownames =FALSE,options =list(pageLength =15, scrollX =TRUE),caption ="Número de casos por ano e classificação.")```