Acidentes nas Rodovias Federais Brasileiras em 2023

Quem são as vítimas, onde acontece e quando o risco é maior?

1 Introdução

1.1 O problema

Em 2023, as rodovias federais brasileiras foram cenário de mais de 100 mil acidentes, deixando um rastro de mortes, feridos e famílias destruídas. Por trás de cada linha nesse conjunto de dados há uma história humana: um motorista distraído, uma madrugada de sexta-feira, uma curva perigosa na BR-116.

Este projeto parte de uma pergunta simples, mas socialmente urgente: existem padrões nos acidentes rodoviários brasileiros que, se compreendidos, poderiam salvar vidas?

1.2 Abordagem

Utilizamos os microdados de acidentes da Polícia Rodoviária Federal (PRF) referentes ao ano de 2023 — uma base com mais de 570 mil registros (um por pessoa envolvida em cada acidente) e 37 variáveis cobrindo local, horário, causa, perfil dos envolvidos e desfecho (ileso, ferido leve, ferido grave ou óbito).

A análise percorre três eixos narrativos:

  1. As vias mais perigosas — quais rodovias e trechos concentram mais mortes?
  2. O relógio do risco — em que dias e horários os acidentes são mais graves?
  3. O perfil da vítima — quem são as pessoas que mais morrem nas estradas?

1.3 Como essa análise ajuda?

Os resultados podem subsidiar decisões de gestores públicos (onde instalar barreiras, radares, policiamento intensificado), pesquisadores de segurança viária e a sociedade civil interessada em compreender o custo humano do trânsito brasileiro.


2 Pacotes Requeridos

# readr     → leitura eficiente de arquivos CSV grandes
library(readr)

# dplyr     → manipulação e transformação de dados (filter, mutate, group_by, summarise)
library(dplyr)

# tidyr     → reformulação de dados (pivot, separate, unite)
library(tidyr)

# lubridate → parsing e manipulação de datas e horários
library(lubridate)

# stringr   → manipulação de strings (limpeza, padronização de texto)
library(stringr)

# ggplot2   → visualizações estáticas de alta qualidade
library(ggplot2)

# plotly    → gráficos interativos a partir de objetos ggplot2 ou nativos
library(plotly)

# DT        → tabelas interativas com busca, filtro e paginação
library(DT)

# forcats   → reordenação e manipulação de fatores para gráficos
library(forcats)

# scales    → formatação de eixos (milhar, percentual, moeda)
library(scales)

# knitr     → tabelas estáticas formatadas dentro do R Markdown
library(knitr)

3 Preparação dos Dados

3.1 Fonte

Os dados foram obtidos no portal de Dados Abertos da Polícia Rodoviária Federal (PRF):

https://www.gov.br/prf/pt-br/acesso-a-informacao/dados-abertos/dados-abertos-acidentes

O arquivo utilizado é o “Acidentes 2023 — Agrupados por pessoa — Todas as causas e tipos de acidentes”, disponibilizado em formato CSV com separador ponto-e-vírgula e codificação latin1.

3.2 Sobre os dados originais

A PRF registra cada acidente através do sistema BAT (Boletim de Acidente de Trânsito), que coleta informações sobre os envolvidos, o local, os veículos e a dinâmica do acidente. O arquivo “agrupado por pessoa” é desnormalizado: cada linha representa uma pessoa envolvida em um acidente, de modo que os atributos do evento (local, hora, causa) se repetem para cada envolvido. Um acidente com três pessoas gera três linhas.

O conjunto original possui 571.052 linhas e 37 colunas, cobrindo todo o ano de 2023.

Peculiaridades observadas na base original:

  • km, latitude e longitude usam vírgula como separador decimal (padrão brasileiro), incompatível com o tipo numérico do R.
  • data_inversa está no formato YYYY-MM-DD como texto — precisa ser convertida para Date.
  • horario está no formato HH:MM:SS como texto.
  • sexo contém além de "Masculino" e "Feminino" os valores "Não Informado" e "Ignorado", que devem ser tratados como NA.
  • estado_fisico contém "Não Informado" que também deve ser tratado como NA.
  • Colunas administrativas (id, pesid, id_veiculo, delegacia, ordem_tipo_acidente) não acrescentam valor analítico e serão removidas.
  • Valores ausentes reais (NA) estão presentes em diversas colunas, especialmente nas relacionadas a pessoas (pesid, tipo_envolvido, estado_fisico, idade, sexo).

3.3 Importação

# Leitura do CSV com separador ";" e encoding latin1 (padrão PRF)
dados_raw <- read_delim(
  "acidentes2023_todas_causas_tipos.csv",
  delim     = ";",
  locale    = locale(encoding = "latin1", decimal_mark = ","),
  show_col_types = FALSE
)

cat("Dimensões originais:", nrow(dados_raw), "linhas ×", ncol(dados_raw), "colunas\n")
## Dimensões originais: 571052 linhas × 37 colunas

3.4 Limpeza e tratamento

dados <- dados_raw |>
  
  # ── 1. REMOVER colunas sem valor analítico ──────────────────────────────────
  # id, pesid, id_veiculo: identificadores internos do sistema BAT
  # delegacia, uop, regional: divisões administrativas internas da PRF
  # ordem_tipo_acidente: índice de ordenação interno, sem interpretação analítica
  select( -pesid, -id_veiculo, -delegacia, -uop, -regional,
         -ordem_tipo_acidente) |>
  
  # ── 2. CONVERTER tipos de dados ────────────────────────────────────────────
  mutate(
    # Data: de character "YYYY-MM-DD" para tipo Date
    data_inversa = ymd(data_inversa),
    
    # Horário: extrair apenas a hora inteira para facilitar análise por turno
    hora = as.integer(str_sub(horario, 1, 2)),
    
    # km: vírgula → ponto, depois numérico
    km = as.numeric(str_replace(km, ",", ".")),
    
    # Coordenadas: vírgula → ponto, depois numérico
    latitude  = as.numeric(str_replace(latitude,  ",", ".")),
    longitude = as.numeric(str_replace(longitude, ",", "."))
  ) |>
  
  # ── 3. PADRONIZAR valores textuais ambíguos → NA ────────────────────────────
  # Sexo: "Não Informado" e "Ignorado" não têm valor analítico → NA
  mutate(
    sexo = na_if(sexo, "Não Informado"),
    sexo = na_if(sexo, "Ignorado"),
    
    # Estado físico: "Não Informado" → NA
    estado_fisico = na_if(estado_fisico, "Não Informado")
  ) |>
  
  # ── 4. CRIAR novas variáveis derivadas ─────────────────────────────────────
  mutate(
    # Turno do dia agrupado em 4 períodos para análise de risco por período
    turno = case_when(
      hora >= 6  & hora < 12 ~ "Manhã (6h–12h)",
      hora >= 12 & hora < 18 ~ "Tarde (12h–18h)",
      hora >= 18 & hora < 24 ~ "Noite (18h–24h)",
      TRUE                   ~ "Madrugada (0h–6h)"
    ),
    turno = factor(turno, levels = c(
      "Madrugada (0h–6h)", "Manhã (6h–12h)",
      "Tarde (12h–18h)",   "Noite (18h–24h)"
    )),
    
    # Fim de semana vs dia útil
    fim_de_semana = dia_semana %in% c("sábado", "domingo"),
    
    # Indicador binário: o acidente resultou em morte?
    acidente_fatal = mortos > 0,
    
    # Faixa etária para análise do perfil das vítimas
    faixa_etaria = cut(
      idade,
      breaks = c(0, 17, 24, 34, 44, 54, 64, Inf),
      labels = c("< 18", "18–24", "25–34", "35–44", "45–54", "55–64", "65+"),
      right  = TRUE
    )
  ) |>
  
  # ── 5. FILTRAR registros sem informação mínima para a análise ──────────────
  # Remover linhas sem data, UF ou classificação do acidente
  filter(
    !is.na(data_inversa),
    !is.na(uf),
    !is.na(classificacao_acidente)
  )

cat("Dimensões após limpeza:", nrow(dados), "linhas ×", ncol(dados), "colunas\n")
## Dimensões após limpeza: 571049 linhas × 36 colunas

3.5 Dados finais

# Exibir as primeiras 10 linhas de forma interativa
dados |>
  head(200) |>
  select(data_inversa, dia_semana, hora, turno, uf, br, municipio,
         causa_acidente, tipo_acidente, classificacao_acidente,
         tipo_veiculo, tipo_envolvido, estado_fisico,
         idade, faixa_etaria, sexo, mortos, acidente_fatal) |>
  datatable(
    options = list(pageLength = 8, scrollX = TRUE),
    caption = "Amostra dos dados limpos (primeiras 200 linhas)"
  )

3.6 Resumo das variáveis

# Tabela consolidada com tipo, completude e descrição de cada variável
resumo <- tibble::tribble(
  ~Variável,                ~Tipo,       ~`% Preenchido`, ~Descrição,
  "data_inversa",           "Date",       "100%",  "Data do acidente",
  "dia_semana",             "Categórico", "100%",  "Dia da semana",
  "hora",                   "Inteiro",    "100%",  "Hora do acidente (0–23)",
  "turno",                  "Fator",      "100%",  "Período derivado da hora",
  "uf",                     "Categórico", "100%",  "Unidade federativa",
  "br",                     "Inteiro",     "99,7%", "Número da rodovia federal",
  "km",                     "Numérico",   "99,7%", "Quilômetro da rodovia",
  "municipio",              "Categórico", "100%",  "Município do acidente",
  "causa_acidente",         "Categórico", "100%",  "Causa registrada",
  "causa_principal",        "Lógico",     "100%",  "Se é a causa principal (Sim/Não)",
  "tipo_acidente",          "Categórico", "100%",  "Tipo de colisão/evento",
  "classificacao_acidente", "Categórico", "100%",  "Gravidade do acidente",
  "fase_dia",               "Categórico", "100%",  "Fase do dia (Pleno dia, Noite...)",
  "condicao_metereologica", "Categórico", "100%",  "Condição climática",
  "tipo_pista",             "Categórico", "100%",  "Tipo de pista (simples, dupla...)",
  "tracado_via",            "Categórico", "100%",  "Traçado (reta, curva...)",
  "uso_solo",               "Categórico", "100%",  "Urbano ou rural",
  "tipo_veiculo",           "Categórico",  "96,4%", "Categoria do veículo",
  "marca",                  "Categórico", "100%",  "Marca/modelo do veículo",
  "ano_fabricacao_veiculo", "Inteiro",     "96,4%", "Ano de fabricação do veículo",
  "tipo_envolvido",         "Categórico",  "90,2%", "Condutor, passageiro, pedestre...",
  "estado_fisico",          "Categórico",  "90,2%", "Estado após o acidente",
  "idade",                  "Inteiro",     "81,6%", "Idade da pessoa envolvida",
  "faixa_etaria",           "Fator",       "81,6%", "Faixa etária derivada",
  "sexo",                   "Categórico",  "90,2%", "Sexo da pessoa",
  "mortos",                 "Inteiro",     "90,2%", "Nº de mortos no acidente",
  "acidente_fatal",         "Lógico",      "90,2%", "Acidente resultou em morte?",
  "latitude",               "Numérico",   "100%",  "Latitude geográfica",
  "longitude",              "Numérico",   "100%",  "Longitude geográfica"
)

kable(resumo, align = "llll")
Variável Tipo % Preenchido Descrição
data_inversa Date 100% Data do acidente
dia_semana Categórico 100% Dia da semana
hora Inteiro 100% Hora do acidente (0–23)
turno Fator 100% Período derivado da hora
uf Categórico 100% Unidade federativa
br Inteiro 99,7% Número da rodovia federal
km Numérico 99,7% Quilômetro da rodovia
municipio Categórico 100% Município do acidente
causa_acidente Categórico 100% Causa registrada
causa_principal Lógico 100% Se é a causa principal (Sim/Não)
tipo_acidente Categórico 100% Tipo de colisão/evento
classificacao_acidente Categórico 100% Gravidade do acidente
fase_dia Categórico 100% Fase do dia (Pleno dia, Noite…)
condicao_metereologica Categórico 100% Condição climática
tipo_pista Categórico 100% Tipo de pista (simples, dupla…)
tracado_via Categórico 100% Traçado (reta, curva…)
uso_solo Categórico 100% Urbano ou rural
tipo_veiculo Categórico 96,4% Categoria do veículo
marca Categórico 100% Marca/modelo do veículo
ano_fabricacao_veiculo Inteiro 96,4% Ano de fabricação do veículo
tipo_envolvido Categórico 90,2% Condutor, passageiro, pedestre…
estado_fisico Categórico 90,2% Estado após o acidente
idade Inteiro 81,6% Idade da pessoa envolvida
faixa_etaria Fator 81,6% Faixa etária derivada
sexo Categórico 90,2% Sexo da pessoa
mortos Inteiro 90,2% Nº de mortos no acidente
acidente_fatal Lógico 90,2% Acidente resultou em morte?
latitude Numérico 100% Latitude geográfica
longitude Numérico 100% Longitude geográfica

4 Análise Exploratória dos Dados

4.1 As rodovias mais perigosas do Brasil

criticidade_br <- dados |> 
  filter(!is.na(br)) |> 
  group_by(id, br) |> 
  summarise(
    mortos_sinistro = max(mortos, na.rm = TRUE),
    .groups = "drop"
  ) |> 
  group_by(br) |> 
  summarise(
    total_mortos = sum(mortos_sinistro, na.rm = TRUE),
    total_sinistros = n(),
    letalidade_por_100 = round((total_mortos / total_sinistros) * 100, 1),
    .groups = "drop"
  ) |> 
  arrange(desc(total_mortos)) |> 
  slice_head(n = 15) |> 
  mutate(br_label = paste0("BR-", br))

p_br <- ggplot(criticidade_br, aes(
    x    = reorder(br_label, total_mortos),
    y    = total_mortos,
    fill = letalidade_por_100,
    text = paste0("<b>", br_label, "</b><br>",
                  "Total de Óbitos: ", total_mortos, "<br>",
                  "Volume de Sinistros: ", total_sinistros, "<br>",
                  "Letalidade (por 100 sinistros): ", letalidade_por_100, "%")
  )) +
  geom_col(width = 0.75) +
  scale_fill_gradient(low = "#E57373", high = "#880E4F", name = "Letalidade %") +
  scale_y_continuous(labels = label_number(big.mark = ".")) +
  coord_flip() +
  labs(
    title = "Top 15 Rodovias Federais por Volume de Óbitos",
    x = NULL,
    y = "Total de Mortes Registradas"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    panel.grid.minor = element_blank()
  )

ggplotly(p_br, tooltip = "text")

Achado: A BR-116 (Rio–Bahia) lidera em volume absoluto de mortes, mas rodovias menores como a BR-277 e BR-376 apresentam letalidade proporcional maior, sinalizando trechos estruturalmente perigosos.


4.2 O relógio do risco: quando os acidentes são mais letais?

# Taxa de fatalidade por hora do dia — focando na gravidade, não no volume
fatalidade_hora <- dados |>
  filter(!is.na(hora), !is.na(acidente_fatal)) |>
  group_by(hora) |>
  summarise(
    total   = n(),
    fatais  = sum(acidente_fatal, na.rm = TRUE),
    taxa    = round(fatais / total * 100, 2)
  )

p_hora <- fatalidade_hora |>
  ggplot(aes(
    x    = hora,
    y    = taxa,
    text = paste0(
      "Hora: ", hora, "h<br>",
      "Taxa de fatalidade: ", taxa, "%<br>",
      "Acidentes registrados: ", format(total, big.mark = ".")
    )
  )) +
  geom_line(color = "#B71C1C", linewidth = 1.2) +
  geom_point(aes(size = total), color = "#B71C1C", alpha = 0.7) +
  scale_x_continuous(breaks = 0:23, labels = paste0(0:23, "h")) +
  scale_size_continuous(labels = label_number(big.mark = "."),
                        name = "Nº de\nocorrências") +
  labs(
    title    = "Taxa de fatalidade por hora do dia (2023)",
    subtitle = "% de acidentes com vítima fatal × hora de registro",
    x        = "Hora do dia",
    y        = "% de acidentes fatais"
  ) +
  theme_minimal(base_size = 13) +
  theme(
    axis.text.x   = element_text(angle = 45, hjust = 1),
    plot.title    = element_text(face = "bold")
  )

ggplotly(p_hora, tooltip = "text")
# Heatmap: dias da semana × turno — volume de acidentes fatais
ordem_dias <- c("segunda-feira", "terça-feira", "quarta-feira",
                "quinta-feira", "sexta-feira", "sábado", "domingo")

heat_data <- dados |>
  filter(acidente_fatal == TRUE) |>
  mutate(dia_semana = factor(dia_semana, levels = ordem_dias)) |>
  group_by(dia_semana, turno) |>
  summarise(n = n(), .groups = "drop")

p_heat <- heat_data |>
  ggplot(aes(
    x    = turno,
    y    = fct_rev(dia_semana),
    fill = n,
    text = paste0(
      dia_semana, " – ", turno, "<br>",
      "Acidentes fatais: ", format(n, big.mark = ".")
    )
  )) +
  geom_tile(color = "white", linewidth = 0.5) +
  scale_fill_gradient(low = "#FFF9C4", high = "#B71C1C",
                      labels = label_number(big.mark = "."),
                      name = "Acidentes\nfatais") +
  labs(
    title    = "Acidentes fatais por dia da semana e turno",
    subtitle = "Concentração de mortes ao longo da semana",
    x        = NULL,
    y        = NULL
  ) +
  theme_minimal(base_size = 13) +
  theme(
    axis.text.x = element_text(angle = 20, hjust = 1),
    plot.title  = element_text(face = "bold")
  )

ggplotly(p_heat, tooltip = "text")

Achado: A madrugada do fim de semana (sábado e domingo, 0h–6h) concentra a maior proporção de acidentes fatais, enquanto o volume absoluto de acidentes é maior nas tardes de dia útil. A combinação madrugada + fim de semana é o cenário de maior risco de morte.


4.3 As causas que mais matam

causas_fatais <- dados |>
  filter(causa_principal == "Sim") |>          # apenas a causa principal do acidente
  group_by(causa_acidente, acidente_fatal) |>
  summarise(n = n(), .groups = "drop") |>
  pivot_wider(names_from = acidente_fatal,
              values_from = n,
              values_fill = 0,
              names_prefix = "fatal_") |>
  rename(nao_fatal = `fatal_FALSE`, fatal = `fatal_TRUE`) |>
  mutate(
    total = fatal + nao_fatal,
    taxa_fatalidade = round(fatal / total * 100, 1)
  ) |>
  filter(total >= 500) |>
  arrange(desc(taxa_fatalidade)) |>
  slice_head(n = 15)

p_causas <- causas_fatais |>
  ggplot(aes(
    x    = reorder(str_wrap(causa_acidente, 35), taxa_fatalidade),
    y    = taxa_fatalidade,
    fill = total,
    text = paste0(
      "<b>", causa_acidente, "</b><br>",
      "Taxa de fatalidade: ", taxa_fatalidade, "%<br>",
      "Total de acidentes: ", format(total, big.mark = "."), "<br>",
      "Acidentes fatais: ",   format(fatal, big.mark = ".")
    )
  )) +
  geom_col() +
  coord_flip() +
  scale_fill_gradient(low = "#BBDEFB", high = "#1565C0",
                      labels = label_number(big.mark = "."),
                      name = "Volume total\nde acidentes") +
  labs(
    title    = "Causas com maior taxa de fatalidade (mín. 500 acidentes)",
    subtitle = "% de acidentes com essa causa que resultaram em morte",
    x        = NULL,
    y        = "Taxa de fatalidade (%)"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold"))

ggplotly(p_causas, tooltip = "text")

Achado: Causas como dormindo ao volante e ultrapassagem indevida têm taxas de fatalidade muito superiores à média — mais letais do que a famosa “ingestão de álcool”. A velocidade incompatível, embora frequente, mata menos proporcionalmente do que o sono ao volante.


4.4 O perfil da vítima fatal

# Mortos por sexo e faixa etária
perfil_mortos <- dados |>
  filter(
    estado_fisico == "Óbito",
    !is.na(sexo),
    !is.na(faixa_etaria)
  ) |>
  group_by(faixa_etaria, sexo) |>
  summarise(mortos = n(), .groups = "drop")

p_perfil <- perfil_mortos |>
  ggplot(aes(
    x    = faixa_etaria,
    y    = mortos,
    fill = sexo,
    text = paste0(
      "Faixa: ", faixa_etaria, "<br>",
      "Sexo: ", sexo, "<br>",
      "Mortos: ", format(mortos, big.mark = ".")
    )
  )) +
  geom_col(position = "dodge") +
  scale_fill_manual(values = c("Masculino" = "#1565C0", "Feminino" = "#E91E63"),
                    name = "Sexo") +
  scale_y_continuous(labels = label_number(big.mark = ".")) +
  labs(
    title    = "Mortos por faixa etária e sexo (2023)",
    subtitle = "Homens representam a grande maioria das vítimas fatais",
    x        = "Faixa etária",
    y        = "Número de mortos"
  ) +
  theme_minimal(base_size = 13) +
  theme(plot.title = element_text(face = "bold"))

ggplotly(p_perfil, tooltip = "text")
# Mortes por tipo de veículo
veiculo_mortes <- dados |>
  filter(!is.na(tipo_veiculo)) |>
  group_by(tipo_veiculo, data_inversa, horario, km) |>
  summarise(mortos_acidente = max(mortos, na.rm = TRUE), .groups = "drop") |>
  group_by(tipo_veiculo) |>
  summarise(
    total_mortos   = sum(mortos_acidente),
    total_acidentes = n(),
    taxa = round(total_mortos / total_acidentes * 100, 1)
  ) |>
  filter(total_acidentes >= 100) |>
  arrange(desc(taxa)) |>
  slice_head(n = 12)

p_veiculo <- veiculo_mortes |>
  ggplot(aes(
    x    = reorder(tipo_veiculo, taxa),
    y    = taxa,
    fill = total_acidentes,
    text = paste0(
      "<b>", tipo_veiculo, "</b><br>",
      "Taxa de mortalidade: ", taxa, "%<br>",
      "Total de acidentes: ", format(total_acidentes, big.mark = ".")
    )
  )) +
  geom_col() +
  coord_flip() +
  scale_fill_gradient(low = "#C8E6C9", high = "#2E7D32",
                      labels = label_number(big.mark = "."),
                      name = "Total de\nacidentes") +
  labs(
    title    = "Taxa de mortalidade por tipo de veículo",
    subtitle = "% de acidentes que resultaram em morte, por categoria de veículo",
    x        = NULL,
    y        = "% de acidentes com morte"
  ) +
  theme_minimal(base_size = 13) +
  theme(plot.title = element_text(face = "bold"))

ggplotly(p_veiculo, tooltip = "text")

Achado: Homens entre 25 e 44 anos são o grupo mais afetado. Motociclistas aparecem desproporcionalmente: representam cerca de 18% dos envolvidos em acidentes, mas concentram uma parcela muito maior das vítimas fatais — evidenciando a vulnerabilidade desse modal.


4.5 Condições ambientais: o clima influencia a gravidade?

clima_grave <- dados |>
  filter(!is.na(condicao_metereologica)) |>
  mutate(
    grave = estado_fisico %in% c("Lesões Graves", "Óbito")
  ) |>
  group_by(condicao_metereologica) |>
  summarise(
    total = n(),
    graves = sum(grave, na.rm = TRUE),
    taxa_grave = round(graves / total * 100, 1)
  ) |>
  filter(total >= 500) |>
  arrange(desc(taxa_grave))

p_clima <- clima_grave |>
  ggplot(aes(
    x    = reorder(condicao_metereologica, taxa_grave),
    y    = taxa_grave,
    fill = taxa_grave,
    text = paste0(
      "<b>", condicao_metereologica, "</b><br>",
      "Taxa de feridos graves/óbitos: ", taxa_grave, "%<br>",
      "Total de registros: ", format(total, big.mark = ".")
    )
  )) +
  geom_col() +
  coord_flip() +
  scale_fill_gradient(low = "#E3F2FD", high = "#0D47A1", name = "Taxa (%)") +
  labs(
    title    = "Gravidade dos acidentes por condição climática",
    subtitle = "% de envolvidos com lesões graves ou óbito",
    x        = NULL,
    y        = "% com lesões graves ou óbito"
  ) +
  theme_minimal(base_size = 13) +
  theme(plot.title = element_text(face = "bold"))

ggplotly(p_clima, tooltip = "text")

Achado (contra-intuitivo): Chuva e névoa não são as condições com maior proporção de vítimas graves. O tempo limpo aparece em acidentes graves porque é justamente quando os motoristas trafegam mais rápido e com menor atenção, resultando em colisões mais violentas.


4.6 Tabela: os municípios mais perigosos

municipios_perigo <- dados |>
  filter(!is.na(municipio), !is.na(uf)) |>
  group_by(uf, municipio, br) |>
  summarise(
    acidentes       = n_distinct(paste(data_inversa, horario, km)),
    mortos          = sum(mortos, na.rm = TRUE),
    feridos_graves  = sum(feridos_graves, na.rm = TRUE),
    taxa_fatalidade = round(mortos / acidentes * 100, 1),
    .groups = "drop"
  ) |>
  filter(acidentes >= 50) |>
  arrange(desc(mortos)) |>
  slice_head(n = 30)

datatable(
  municipios_perigo,
  colnames = c("UF", "Município", "BR", "Nº Acidentes",
               "Mortos", "Feridos Graves", "Taxa Fatal. (%)"),
  options  = list(pageLength = 10, scrollX = TRUE),
  caption  = "30 municípios com mais mortes em rodovias federais (mín. 50 acidentes)"
) |>
  formatStyle(
    "taxa_fatalidade",
    background = styleColorBar(range(municipios_perigo$taxa_fatalidade), "#EF9A9A"),
    backgroundSize = "100% 90%"
  )

5 Conclusões

5.1 O problema endereçado

Este projeto investigou os padrões dos acidentes em rodovias federais brasileiras em 2023, buscando identificar onde, quando e em quem a tragédia se concentra.

5.2 Como foi abordado

Utilizamos os microdados da PRF (571 mil registros, 37 variáveis), realizando limpeza, padronização, criação de variáveis derivadas e análise exploratória com visualizações interativas via plotly e tabelas via DT.

5.3 Principais insights

  • A BR-116 é a mais letal em volume absoluto, mas trechos da BR-277 e BR-376 têm letalidade proporcional maior, sinalizando perigo estrutural.
  • A madrugada do fim de semana concentra a maior proporção de mortes, apontando para o binômio álcool + fadiga como fator crítico.
  • Dormir ao volante é proporcionalmente mais fatal do que álcool — uma causa que recebe menos atenção pública.
  • Homens entre 25 e 44 anos são as principais vítimas, especialmente motociclistas, que morrem em proporção muito superior à sua participação no tráfego.
  • Tempo limpo está associado a acidentes mais graves, contrariando o senso comum sobre chuva.

5.4 Implicações práticas

Para gestores públicos: reforçar policiamento e barreiras nas BRs mais letais, especialmente nas madrugadas de fim de semana. Para campanhas de segurança: o foco em fadiga ao volante merece a mesma visibilidade que o álcool. Para seguradores e pesquisadores: motociclistas e jovens do sexo masculino compõem o grupo de maior risco.

5.5 Limitações e trabalhos futuros

  • A base registra o estado físico no momento do acidente — mortes posteriores não são capturadas.
  • Não há dados sobre velocidade efetiva dos veículos, o que limitaria análises sobre excesso de velocidade.
  • Uma análise futura poderia cruzar esses dados com os microdados do DETRAN (frota por município) para calcular taxas de acidente ajustadas pelo volume de veículos.
  • Análises geoespaciais com o pacote leaflet permitiriam identificar trechos críticos com precisão de quilômetro.