Acidentes nas Estradas Brasileiras: estamos seguros?

Introdução

Acidentes rodoviários

Os acidentes rodoviários nas BRs (rodovias federais) do Brasil representam um dos maiores problemas de segurança pública e de saúde no país. Anualmente, dezenas de milhares de acidentes são registrados nessas rodovias, resultando em muitas mortes e centenas de milhares de feridos. Em muitos anos, as BRs concentram mais de 60% das mortes registradas em rodovia.

Estudar as causas e motivos dos acidentes de forma estatística é fundamental porque permite identificar padrões espaciais e temporais (dias da semana, horários, condições climáticas e trechos mais perigosos), além de apontar perfis de risco envolvendo veículos, condutores e infraestrutura. Com essas informações, é possível desenvolver políticas públicas mais eficientes, planejar investimentos em engenharia viária, melhorar a fiscalização e criar campanhas educativas baseadas em evidências. A análise estatística transforma dados em informação estratégica, permitindo que ações sejam direcionadas exatamente aos pontos e comportamentos mais críticos, o que contribui para a redução de mortes, dos custos hospitalares e dos prejuízos econômicos associados aos acidentes de trânsito.

Origem dos dados

O dataset utilizado para análise provém de dados públicos achados no Kaggle, relacionados a acidentes ocorridos nas rodovias federais brasileiras (https://www.kaggle.com/datasets/pedrogoncalv/brazilian-traffic-incidents-2007-to-2023). Ele cobre o período de 2007 a 2023, ou seja, inclui mais de uma década de registros, o que permite análises de longo prazo, tendências históricas e mudanças ao longo do tempo. Por ser uma base pública, os dados podem ser utilizados livremente (respeitando termos de uso) para pesquisa, análise estatística, planejamento de políticas públicas, entre outros.

Estrutura dos dados

O dataset contém um número muito grande de registros — em ordem de ≈ 1,8 milhão de acidentes no período coberto. As principais variáveis (colunas) incluem:

  • Data do acidente e dia da semana: possibilitando análise temporal (padrões ao longo dos anos, sazonalidades, dias da semana com mais incidência, horas do dia, etc.);
  • Localização geográfica: estado da federação, rodovia/BR, — permitindo análises espaciais e a comparação entre estados, rodovias, regiõe;
  • Causa do acidente, ou seja, a razão registrada pela ocorrência (colisão traseira, saída de pista, tombamento, etc.), essencial para estudar fatores de risco;
  • Tipo/classificação do acidente; descreve como o acidente ocorreu (colisão, atropelamento, capotamento, saída de pista, etc.);
  • Número de veículos envolvidos — útil para entender a gravidade, tipo de incidente, contribuições de colisões múltiplas, etc;
  • Número de pessoas envolvidas e gravidade dos ferimentos: total de pessoas, além de desagregações como número de mortos, feridos leves, feridos graves.

Importância da base para análise

Usar esse dataset é vantajoso para diversos propósitos de análise de acidentes rodoviários:

  • Amplitude temporal e espacial — com mais de 15 anos de histórico e cobertura nacional, você pode identificar padrões que se repetem no tempo (como sazonalidades, efeitos de mudanças de legislação, de condições de infraestrutura, etc.) e no espaço (estados, regiões, rodovias mais perigosas).

  • Granularidade dos dados — ao ter informação de causa, tipo, número de veículos/pessoas, gravidade, você pode segmentar os dados para estudar diferentes aspectos: acidentes fatais vs não-fatais; colisões vs saídas de pista; influência de múltiplos veículos, e assim por diante.

  • Capacidade de modelagem/estatística — esses dados permitem análises estatísticas como regressões, detecção de “hotspots”, estudos de correlação entre fatores (por exemplo, tipo de rodovia, causa, dia da semana) e gravidade do acidente. Já há pesquisas comprovando a utilidade dessa base para prever risco de acidentes graves ou mortes.

  • Relevância social e de políticas públicas — os resultados extraídos a partir dessa base podem subsidiar discussões sobre segurança viária, priorização de trechos para intervenção, campanhas educativas, fiscalização, melhorias na infraestrutura, entre outros. Isso confere ao seu estudo um caráter não só acadêmico, mas socialmente útil e de impacto.

Pacotes Necessários

Os pacotes a seguir são necessários para a execução do código que você verá nesse projeto. Sem eles, o código aqui demonstrado não será executado com sucesso.

Lib Uso
readr Ler e escrever arquivos de dados de forma rápida e eficiente.
dplyr Fornece funções simples e poderosas para manipular, filtrar, agrupar e resumir dados em dataframes.
purrr Facilita o uso de funções de programação funcional, como aplicar funções a listas e vetores de forma consistente.
stringr Oferece ferramentas para manipulação e tratamento de textos (strings) de maneira padronizada.
lubridate Simplifica a manipulação e conversão de datas e horários no R.
tidyr Serve para organizar e estruturar dados em formatos mais adequados para análise.
ggplot2 Biblioteca para criação de gráficos estatísticos personalizados e de alta qualidade.
knitr Permite gerar relatórios automatizados em R Markdown, convertendo código e resultados em documentos formatados.
forcats Facilitar a manipulação e reorganização de variáveis categóricas.

Preparação dos dados

Leitura dos dados

Os dados disponibilizados pelo Kaggle estão divididos em CSVs por ano, ou seja, os dados dos acidentes dos anos 2007-2023 estão contidos em um CSV para cada ano (17 arquivos).

Primeiro é feito a busca por todos os arquivos em formato CSV que estão na pasta “dados_PRF”:

files <- list.files("dados_PRF/", pattern = "\\.csv$", full.names = TRUE)


cat("Arquivos encontrados:", length(files), "\n")
## Arquivos encontrados: 17

Unificação do dataset

Foi observado que a quantidade de features (colunas) de cada arquivo pode variar, tendo alguns anos que contém menos features que outros. Por isso, também foi realizada a identificação das colunas em comum e diferentes de todos os arquivos.

cols_all <- map(files, ~ names(read_csv2(.x, n_max = 0)))
unique_cols <- unique(unlist(cols_all))

cat("Número total de colunas identificadas:", length(unique_cols), "\n")
anos_arquivos <- str_extract(files, "(?<=_)[0-9]{4}(?=\\.csv$)")
anos_arquivos <- as.numeric(anos_arquivos)

cat("Anos extraídos dos arquivos:", paste(anos_arquivos, collapse = ", "), "\n")
## Anos extraídos dos arquivos: 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023

Abaixo, a função read_prf, é usada para ler os arquivos CSV de forma padronizada, garantindo que todos tenham a mesma estrutura. Ela importa o arquivo tratando todas as colunas como texto para evitar problemas de tipos de dados e usa a codificação latin1 com vírgula como separador decimal. Em seguida, a função verifica se faltam colunas em relação a uma lista pré-definida (all_cols) e, caso estejam ausentes, cria essas colunas preenchidas com valores NA. Por fim, ela reorganiza as colunas para que fiquem exatamente na mesma ordem em todos os arquivos, facilitando a posterior junção e análise dos dados.

#read_prf <- function(file, all_cols) {
read_prf <- function(file, all_cols, ano_arquivo = NULL) { 
  df <- read_csv2(
    file,
    col_types = cols(.default = col_character()),  # tudo como texto para evitar erros
    locale = locale(encoding = "latin1", decimal_mark = ",")
  )
  
  # adicionar colunas faltantes
  missing_cols <- setdiff(all_cols, names(df))
  for (col in missing_cols) {
    df[[col]] <- NA_character_
  }

  # Adicionar ou preencher a coluna "ano" com o ano do arquivo
  if (!is.null(ano_arquivo)) {
    # Se a coluna "ano" não existe, cria
    if (!"ano" %in% names(df)) {
      df$ano <- as.character(ano_arquivo)
    } else {
      # Se existe mas tem valores NA, preenche
      df$ano <- ifelse(is.na(df$ano) | nchar(trimws(df$ano)) == 0, 
                       as.character(ano_arquivo), 
                       df$ano)
    }
  }
  
  df <- df[, all_cols]
  
  return(df)
}

Já nesse trecho de código, é aplicada a função read_prf a todos os arquivos listados usando map, gerando uma lista de data frames padronizados em df_list. Em seguida, ele une todos esses data frames em um único conjunto de dados chamado df_raw por meio de bind_rows, empilhando as linhas de todos os arquivos já com as mesmas colunas e na mesma ordem.

#df_list <- map(files, read_prf, all_cols = unique_cols)
df_list <- map2(files, anos_arquivos, ~ read_prf(.x, all_cols = unique_cols, ano_arquivo = .y))

# unir tudo sem erros
df_raw <- bind_rows(df_list)

cat("Dataset unificado com sucesso!\n")
## Dataset unificado com sucesso!
cat("Total de linhas:", nrow(df_raw), "\n")
## Total de linhas: 2013757

Conversões

Em decorrência das diferenças nos arquivos, foi necessário realizar uma padronização de algumas features, como as datas dos acidentes, o horário dos acidentes e algumas colunas que agora precisamos que sejam numéricas, tais quais o id, br, ano, pessoas, mortos, feridos_leves, feridos_graves, ilesos, ignorados, feridos, veiculos, latitude e longitude.

# conversão de datas
if ("data_inversa" %in% names(df_raw)) {
  df_raw$data_inversa <- dmy(df_raw$data_inversa)
}

# conversão de horário
if ("horario" %in% names(df_raw)) {
  df_raw$horario <- parse_time(df_raw$horario)
}

# colunas que desejamos tornar numéricas
numeric_cols <- intersect(
  c("id","br","ano","pessoas","mortos","feridos_leves","feridos_graves",
    "ilesos","ignorados","feridos","veiculos","latitude","longitude"),
  names(df_raw)
)

df_raw <- df_raw %>%
  mutate(across(all_of(numeric_cols), ~ parse_number(.x)))

Ademais, também foi necessário converter algumas features para pontos flutuantes, como o km.

if ("km" %in% names(df_raw)) {
  df_raw <- df_raw %>%
    mutate(
      km = str_replace(km, ",", "."),
      km = parse_number(km)
    )
}

Limpeza dos dados

Para a limpeza dos dados, primeiro, foram removidos espaços extras entre as palavras nas colunas de tipo texto, deixando os dados mais limpos e consistentes.

df_raw <- df_raw %>%
  mutate(across(where(is.character), ~ str_squish(.x)))

Por fim, o objeto df_raw é atribuído a df, que passa a ser o dataset principal para análise, e o código imprime no console uma mensagem de finalização junto com o total de linhas e colunas, confirmando que o conjunto de dados está pronto para uso.

df <- df_raw

cat("\nFinalizado! Dataset pronto para análise.\n")
## 
## Finalizado! Dataset pronto para análise.
cat("Total de linhas:", nrow(df), "\n")
## Total de linhas: 2013757
cat("Total de colunas:", ncol(df), "\n")
## Total de colunas: 31

Base finalizada

Abaixo está as características do conjunto de dados finalizado após todo o tratamento de dados e pronto para realizas as análises necessárias.

##        id            data_inversa         dia_semana       
##  Min.   :       8   Min.   :2007-01-01   Length:2013757    
##  1st Qu.:  376464   1st Qu.:2008-09-17   Class :character  
##  Median :  775489   Median :2010-02-17   Mode  :character  
##  Mean   :19961284   Mean   :2010-06-14                     
##  3rd Qu.: 1304960   3rd Qu.:2011-04-30                     
##  Max.   :83529889   Max.   :2016-12-31                     
##                     NA's   :1114235                        
##     horario                     uf                  br              km        
##  Min.   :00:00:00.000000   Length:2013757     Min.   :  0.0   Min.   :-870.3  
##  1st Qu.:08:40:00.000000   Class :character   1st Qu.:101.0   1st Qu.:  79.0  
##  Median :14:10:00.000000   Mode  :character   Median :158.0   Median : 201.6  
##  Mean   :13:24:56.887294                      Mean   :212.4   Mean   : 262.5  
##  3rd Qu.:18:15:00.000000                      3rd Qu.:324.0   3rd Qu.: 412.0  
##  Max.   :23:59:00.000000                      Max.   :958.0   Max.   :9967.1  
##                                               NA's   :972     NA's   :972     
##   municipio         causa_acidente     tipo_acidente     
##  Length:2013757     Length:2013757     Length:2013757    
##  Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character  
##                                                          
##                                                          
##                                                          
##                                                          
##  classificacao_acidente   fase_dia         sentido_via       
##  Length:2013757         Length:2013757     Length:2013757    
##  Class :character       Class :character   Class :character  
##  Mode  :character       Mode  :character   Mode  :character  
##                                                              
##                                                              
##                                                              
##                                                              
##  condicao_metereologica  tipo_pista        tracado_via       
##  Length:2013757         Length:2013757     Length:2013757    
##  Class :character       Class :character   Class :character  
##  Mode  :character       Mode  :character   Mode  :character  
##                                                              
##                                                              
##                                                              
##                                                              
##    uso_solo              ano          pessoas            mortos        
##  Length:2013757     Min.   :2007   Min.   :  1.000   Min.   : 0.00000  
##  Class :character   1st Qu.:2010   1st Qu.:  1.000   1st Qu.: 0.00000  
##  Mode  :character   Median :2013   Median :  2.000   Median : 0.00000  
##                     Mean   :2013   Mean   :  2.202   Mean   : 0.05588  
##                     3rd Qu.:2016   3rd Qu.:  2.000   3rd Qu.: 0.00000  
##                     Max.   :2023   Max.   :248.000   Max.   :33.00000  
##                                                                        
##  feridos_leves     feridos_graves         ilesos         ignorados       
##  Min.   : 0.0000   Min.   :  0.0000   Min.   : 0.000   Min.   : 0.00000  
##  1st Qu.: 0.0000   1st Qu.:  0.0000   1st Qu.: 1.000   1st Qu.: 0.00000  
##  Median : 0.0000   Median :  0.0000   Median : 1.000   Median : 0.00000  
##  Mean   : 0.5343   Mean   :  0.1855   Mean   : 1.327   Mean   : 0.09885  
##  3rd Qu.: 1.0000   3rd Qu.:  0.0000   3rd Qu.: 2.000   3rd Qu.: 0.00000  
##  Max.   :61.0000   Max.   :222.0000   Max.   :99.000   Max.   :86.00000  
##                                                                          
##     feridos            veiculos         latitude            longitude         
##  Min.   :  0.0000   Min.   : 1.000   Min.   :-3.209e+11   Min.   :-6.906e+11  
##  1st Qu.:  0.0000   1st Qu.: 1.000   1st Qu.:-2.003e+09   1st Qu.:-4.546e+09  
##  Median :  0.0000   Median : 2.000   Median :-2.994e+07   Median :-6.196e+07  
##  Mean   :  0.7198   Mean   : 1.721   Mean   :-1.028e+09   Mean   :-2.555e+09  
##  3rd Qu.:  1.0000   3rd Qu.: 2.000   3rd Qu.:-7.006e+05   3rd Qu.:-3.590e+06  
##  Max.   :239.0000   Max.   :25.000   Max.   : 3.836e+10   Max.   : 3.137e+10  
##                                      NA's   :1562200      NA's   :1562200     
##    regional          delegacia             uop           
##  Length:2013757     Length:2013757     Length:2013757    
##  Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character  
##                                                          
##                                                          
##                                                          
## 

Análise Exploratória dos Dados

Análises por ano

Acidentes por ano

Observa-se um crescimento constante no número de acidentes entre 2007 e 2014, atingindo o pico em 2011 com 192.326 ocorrências. Este período foi marcado por aumento significativo da frota veicular e da mobilidade no país, fatores que podem explicar a elevação nos números absolutos.

Além disso, a partir de 2015, há uma queda drástica nos registros, com redução de aproximadamente 35% entre 2014 e 2015. Esta tendência de declínio se mantém até 2020. Possíveis explicações incluem:

  • Implementação da Lei Seca mais rigorosa (2012) com efeitos acumulativos

  • Melhorias na infraestrutura rodoviária

  • Campanhas educativas de segurança no trânsito

  • Avanços tecnológicos em segurança veicular

Ademais, também podemos traçar paralelos com a pandemia do COVID-19, pois ano de 2020 apresenta a menor quantidade de acidentes (63.576), reflexo direto das restrições de mobilidade durante a pandemia. A recuperação parcial em 2021-2022, seguida pela queda em 2023 (dados parciais), sugere um novo patamar de ocorrências inferior aos níveis pré-pandemia.

# acidentes por ano
if ("ano" %in% names(df)) {
  acidentes_ano <- df %>%
    group_by(ano) %>%
    summarise(total = n(), mortos = sum(mortos, na.rm = TRUE))
  print(acidentes_ano)
}
df %>%
  filter(!is.na(ano)) %>%
  count(ano, name = "n_acidentes") %>%
  ggplot(aes(x = ano, y = n_acidentes)) +
  geom_line() +
  geom_point() +
  scale_y_continuous(labels = scales::label_number(scale = 1e-3, suffix = " mil")) +
  labs(
    title = "Número de acidentes por ano",
    x = "Ano",
    y = "Acidentes (em milhares)"
  ) +
  theme_minimal()

Mortes por ano

Já quando analisamos as mortes por ano, os dados revelam um preocupante número entre 2010 e 2012, com oscilações entre 8.623 e 8.675 mortes anuais. O pico absoluto ocorreu em 2011 com 8.675 óbitos, ano que também registrou o maior número total de acidentes (192.326).

Observa-se o início de uma tendência de queda a partir de 2013, que se intensifica significativamente a partir de 2015. A redução de 8.426 mortes em 2013 para 6.867 em 2015 representa uma diminuição de aproximadamente 18,5% em apenas dois anos. Este período coincide com a implementação mais rigorosa da Lei Seca (Lei 12.760/2012) e o início do Plano Nacional de Redução de Mortes no Trânsito.

# Mortes por ano
df %>%
  filter(!is.na(ano), !is.na(mortos)) %>%
  group_by(ano) %>%
  summarise(total_mortos = sum(mortos, na.rm = TRUE)) %>%
  ggplot(aes(x = ano, y = total_mortos)) +
  geom_line(color = "red", linewidth = 1) +
  geom_point(color = "darkred", size = 3) +
  scale_x_continuous(breaks = seq(min(df$ano, na.rm = TRUE), max(df$ano, na.rm = TRUE), by = 2)) +
  scale_y_continuous(
    labels = scales::label_number(scale = 1e-3, suffix = " mil"),
    limits = c(0, NA)
  ) +
  labs(
    title = "Número de mortes em acidentes rodoviários por ano",
    subtitle = "Dados da PRF (2007-2023)",
    x = "Ano",
    y = "Mortes (em milhares)",
    caption = "Fonte: Polícia Rodoviária Federal"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5, color = "gray50"),
    axis.text = element_text(size = 10),
    axis.title = element_text(face = "bold", size = 12),
    panel.grid.minor = element_blank()
  )

Análises por locais

Acidentes por municípios

Os 10 municípios mais perigosos concentram aproximadamente 10-15% do total de acidentes rodoviários do país. Curitiba lidera com impressionantes 27.608 ocorrências, seguida por São José (SC) com 23.081 e Guarulhos com 21.110.

Características Comuns:

  1. Grandes centros urbanos: Todos são municípios com alta densidade populacional

  2. Polos industriais/logísticos: Muitos são importantes centros econômicos (Curitiba, Guarulhos, Betim)

  3. Cidades satélites/cidades-dormitório: Municípios do entorno das capitais (São José, Palhoça, Cariacica)

  4. Localização em rodovias estratégicas: Situados em eixos rodoviários importantes

# Top 10 municípios mais perigosos
if ("municipio" %in% names(df)) {
  top_mun <- df %>%
    count(municipio, sort = TRUE) %>%
    slice_head(n = 10) %>%
    mutate(
      municipio = fct_reorder(municipio, n),
      label = format(n, big.mark = ".", decimal.mark = ",")
    )
  
  # Gráfico de barras horizontal
  p <- ggplot(top_mun, aes(x = n, y = municipio)) +
    geom_col(fill = "firebrick", alpha = 0.8, width = 0.7) +
    geom_text(
      aes(label = label),
      hjust = -0.2,
      size = 4.5,
      fontface = "bold"
    ) +
    scale_x_continuous(
      expand = expansion(mult = c(0, 0.15)),
      labels = scales::label_number(scale = 1e-3, suffix = "k"),
      limits = c(0, max(top_mun$n) * 1.15)
    ) +
    labs(
      title = "Top 10 Municípios com Maior Número de Acidentes Rodoviários",
      subtitle = "Período: 2007-2023 | Dados da Polícia Rodoviária Federal",
      x = "Número Total de Acidentes",
      y = "Município",
      caption = paste("Total de registros:", format(nrow(df), big.mark = ".", decimal.mark = ","))
    ) +
    theme_minimal(base_size = 14) +
    theme(
      plot.title = element_text(face = "bold", size = 18, hjust = 0.5),
      plot.subtitle = element_text(hjust = 0.5, color = "gray50", size = 12),
      axis.text = element_text(size = 12),
      axis.title = element_text(face = "bold", size = 14),
      axis.text.y = element_text(face = "bold"),
      panel.grid.major.y = element_blank(),
      plot.margin = margin(20, 40, 20, 20)
    )
  
  print(p)
}

Acidentes por Unidade Federativa (UF)

Após analisar o gráfico de acidentes por Unidade Federativa, vemos uma surpresa com Minas Gerais liderando, pois não é o estado mais populoso nem com maior PIB. Isso sugere:

  • Extensa malha rodoviária (maior do Brasil)

  • Topografia acidentada (serras e curvas perigosas)

  • Intenso transporte de minério (caminhões pesados)

Ademais, Santa Catarina (2º) e Paraná (3º) superaram São Paulo, indicando:

  • Corredores logísticos intensos para o Mercosul

  • Turismo sazonal massivo (especialmente em SC)

  • Condições climáticas adversas (neblina, chuva)

  • Transporte de commodities agrícolas

df <- df %>%
  mutate(
    total_vitimas = mortos + feridos_leves + feridos_graves,
    grave = if_else(mortos > 0 | feridos_graves > 0, TRUE, FALSE)
  )
# Calcular estatísticas por UF
top_uf <- df %>%
  group_by(uf) %>%
  summarise(
    n = n(),
    n_graves = sum(grave)
  ) %>%
  arrange(desc(n))

ggplot(top_uf, aes(x = reorder(uf, n), y = n)) +
  geom_col() +
  coord_flip() +
  scale_y_continuous(labels = scales::label_number(scale = 1e-3, suffix = " mil")) +
  labs(
    title = "Acidentes por Unidade Federativa",
    x = "UF",
    y = "Número de acidentes"
  )

Análises por BR

Baseado no ranking apresentado, podemos identificar padrões importantes que explicam por que estas rodovias estão entre as mais perigosas do Brasil:

BR-101 e BR-116

BR-101 (1ª posição): Com 4.771 km de extensão, é a rodovia longitudinal mais importante do país, ligando o Rio Grande do Sul ao Rio Grande do Norte. Passa por 12 estados e 337 municípios, sendo vital para o transporte de carga, turismo e deslocamento populacional. Seus trechos mais críticos incluem as regiões metropolitanas de São Paulo, Rio de Janeiro, Salvador e Recife, onde o intenso tráfego urbano se soma ao fluxo interestadual.

BR-116 (2ª posição): Conhecida como “Rodovia Presidente Dutra” no trecho Rio-São Paulo e “Rodovia Régis Bittencourt” no trecho paulista, é a principal ligação norte-sul do país. Seu trecho mais perigoso é a Serra das Araras (RJ), famosa por acidentes graves, além dos trechos urbanos nas grandes capitais.

Corredores Econômicos Críticos (BR-381, BR-040 e BR-153)

BR-381 (3ª) - “Fernão Dias”: Liga São Paulo a Belo Horizonte, sendo o principal corredor de exportação do Sudeste. Intenso tráfego de caminhões carregados com commodities, trechos serranos perigosos e neblina frequente explicam sua alta periculosidade.

BR-040 (4ª) - Rio-Brasília: Conexão vital entre duas das maiores economias do país, com tráfego misto de turistas, carga e veículos de passeio. O trecho da Serra do Mar no Rio de Janeiro é particularmente desafiador.

BR-153 (5ª) - “Transbrasiliana”: Corta o país de norte a sul pelo interior, passando por importantes regiões agrícolas. O transporte de grãos e a falta de infraestrutura adequada em muitos trechos contribuem para os acidentes.

df <- df %>%
  mutate(
    ano = year(data_inversa),
    mes = month(data_inversa),
    dia = day(data_inversa)
  )
# Verificar se a coluna 'br' existe
if ("br" %in% names(df)) {
  
  # Top 20 BRs com mais acidentes
  top_br <- df %>%
    filter(!is.na(br)) %>%
    count(br, sort = TRUE) %>%
    slice_head(n = 20) %>%
    mutate(
      br_label = paste0("BR-", br),
      br_label = fct_reorder(br_label, n),
      label = format(n, big.mark = ".", decimal.mark = ",")
    )
  
  # Gráfico 1: Top 20 BRs (barras horizontais)
  p1 <- ggplot(top_br, aes(x = n, y = br_label)) +
    geom_col(fill = "#2E86AB", alpha = 0.85, width = 0.7) +
    geom_text(
      aes(label = label),
      hjust = -0.2,
      size = 4,
      fontface = "bold",
      color = "darkblue"
    ) +
    scale_x_continuous(
      expand = expansion(mult = c(0, 0.15)),
      labels = scales::label_number(scale = 1e-3, suffix = "k"),
      limits = c(0, max(top_br$n) * 1.15)
    ) +
    labs(
      title = "Top 20 Rodovias Federais com Maior Número de Acidentes",
      subtitle = "Período: 2007-2023 | Dados da PRF",
      x = "Número Total de Acidentes",
      y = "Rodovia Federal",
      caption = paste("Total de registros analisados:", format(nrow(df %>% filter(!is.na(br))), big.mark = ".", decimal.mark = ","))
    ) +
    theme_minimal(base_size = 13) +
    theme(
      plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
      plot.subtitle = element_text(hjust = 0.5, color = "gray50", size = 11),
      axis.text = element_text(size = 11),
      axis.title = element_text(face = "bold", size = 12),
      axis.text.y = element_text(face = "bold"),
      panel.grid.major.y = element_blank(),
      plot.margin = margin(20, 40, 20, 20),
      panel.background = element_rect(fill = "white", color = NA),
      plot.background = element_rect(fill = "white", color = NA)
    )
  
  print(p1)
  
  # Gráfico 2: Evolução temporal das TOP 5 BRs
  top5_brs <- top_br$br[1:5]
  
  p2 <- df %>%
    filter(br %in% top5_brs, !is.na(ano)) %>%
    count(br, ano) %>%
    mutate(
      br_label = paste0("BR-", br)
    ) %>%
    ggplot(aes(x = ano, y = n, color = br_label, group = br_label)) +
    geom_line(linewidth = 1.2, alpha = 0.8) +
    geom_point(size = 2.5) +
    geom_smooth(method = "loess", se = FALSE, linetype = "dashed", alpha = 0.3) +
    scale_x_continuous(
      breaks = seq(2007, 2023, by = 2),
      limits = c(2007, 2023)
    ) +
    scale_y_continuous(
      labels = scales::label_number(scale = 1e-3, suffix = "k")
    ) +
    scale_color_brewer(palette = "Set1", name = "Rodovia") +
    labs(
      title = "Evolução Temporal das 5 Rodovias Mais Perigosas",
      subtitle = "Tendência de acidentes por ano (linha tracejada: tendência)",
      x = "Ano",
      y = "Número de Acidentes"
    ) +
    theme_minimal(base_size = 13) +
    theme(
      plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
      plot.subtitle = element_text(hjust = 0.5, color = "gray50", size = 11),
      legend.position = "bottom",
      legend.title = element_text(face = "bold")
    )
  
  print(p2)
}

Análises por dia da semana

Para analisar os principais dias da semana em que ocorrem acidentes, foi necessário realizar uma padronização da coluna dia_semana do dataset, para que a comparação fosse correta.

df <- df %>%
  mutate(
    dia_semana = str_to_lower(dia_semana),
    dia_semana = str_replace_all(dia_semana, "[^a-zà-ú ]", " "),
    dia_semana = str_squish(dia_semana),

    dia_semana = case_when(
      str_detect(dia_semana, "domingo") ~ "Domingo",
      str_detect(dia_semana, "segunda") ~ "Segunda",
      str_detect(dia_semana, "terca") | str_detect(dia_semana, "terça") ~ "Terça",
      str_detect(dia_semana, "quarta") ~ "Quarta",
      str_detect(dia_semana, "quinta") ~ "Quinta",
      str_detect(dia_semana, "sexta") ~ "Sexta",
      str_detect(dia_semana, "sabado") | str_detect(dia_semana, "sábado") ~ "Sábado",
      TRUE ~ NA_character_
    ),

    dia_semana = factor(
      dia_semana,
      levels = c("Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"),
      ordered = TRUE
    )
  )

O padrão semanal de acidentes rodoviários segue uma lógica social e comportamental bem definida, com a sexta-feira emergindo como o dia mais crítico, seguida pelo sábado, domingo, segunda, quinta, quarta e finalmente terça-feira, o dia mais seguro. Este ranking revela uma dinâmica onde o risco aumenta progressivamente conforme nos aproximamos do fim de semana, atinge seu pico na sexta-feira, mantém-se elevado durante o sábado e domingo, e depois declina gradualmente ao longo da semana seguinte.

A predominância da sexta-feira como o dia com maior número de acidentes representa uma convergência perigosa de múltiplos fatores. Trata-se de um dia de transição onde se misturam as obrigações do trabalho com as expectativas do lazer. O rush hour vespertino é intensificado pela pressa de quem quer iniciar o fim de semana antecipadamente, somando-se ao início das viagens de turismo e ao cansaço acumulado de cinco dias de trabalho. À noite, as happy hours e eventos sociais introduzem o elemento álcool, criando uma combinação particularmente perigosa que se estende até a madrugada.

O sábado mantém uma alta incidência de acidentes, porém com características distintas. O tráfego é predominantemente de lazer e turismo, com famílias inteiras deslocando-se para passeios e visitas. As viagens tendem a ser mais longas e realizadas por motoristas menos experientes em estradas, enquanto à noite persiste o risco associado a eventos sociais e consumo de álcool. Já o domingo apresenta seu próprio padrão, com picos de acidentes concentrados no final da tarde e início da noite, quando ocorre o retorno massivo das viagens de fim de semana, frequentemente marcado por pressa e cansaço acumulado.

df %>%
  count(dia_semana, name = "n") %>%
  ggplot(aes(x = dia_semana, y = n)) +
  geom_col() +
  scale_y_continuous(labels = scales::label_number(scale = 1e-3, suffix = " mil")) +
  labs(
    title = "Acidentes por dia da semana",
    x = "Dia da semana",
    y = "Quantidade de acidentes"
  )

Análises por causa do acidente

O ranking abaixo apresentado revela padrões importantes sobre a vulnerabilidade dos pedestres no trânsito brasileiro e os fatores que contribuem para os acidentes:

  1. Pedestre na Pista

Esta causa genérica lidera porque abrange múltiplas situações onde o pedestre ocupa o espaço destinado aos veículos. Reflete um problema fundamental de infraestrutura inadequada e falta de segregação entre os diferentes modais de transporte. Em muitas cidades brasileiras, calçadas inexistentes ou em mau estado forçam os pedestres a caminhar na pista.

  1. Entrada Inoportuna do Pedestre

Indica problemas de percepção de risco e julgamento inadequado por parte dos pedestres. Muitos subestimam a velocidade dos veículos ou não conseguem calcular corretamente o tempo necessário para a travessia. Este comportamento pode estar relacionado a distrações (celular, fones de ouvido) ou desconhecimento das regras de trânsito.

  1. Pedestre Cruzava Pista Fora da Faixa

Demonstra uma cultura de desrespeito às regras estabelecidas. Pedestres frequentemente optam pelo caminho mais curto em vez do mais seguro. Esta prática é agravada por:

  • Distância excessiva entre faixas de pedestres

  • Falta de sinalização adequada

  • Impaciência e pressa no deslocamento urbano

  1. Transitar na Contramão

Embora menos comum, indica situações extremas de comportamento de risco. Pode estar associado a pedestres embriagados, com problemas mentais, ou em situações de desespero/emergência.

  1. Ingestão de Álcool ou Substâncias Psicoativas pelo Pedestre

Revela que a embriaguez não é exclusiva dos motoristas. Pedestres alcoolizados têm reflexos diminuídos, percepção comprometida e tomam decisões imprudentes. A combinação de álcool e trânsito é perigosa para todos os usuários da via.

  1. Iluminação Deficiente

Fator ambiental e de infraestrutura crítico. A falta de iluminação pública adequada em muitas cidades brasileiras reduz a visibilidade noturna, aumentando o risco de atropelamentos. Pedestres usando roupas escuras tornam-se praticamente invisíveis.

df %>%
  group_by(causa_acidente) %>%
  summarise(
    n = n(),
    n_graves = sum(grave, na.rm = TRUE),
    proporcao_grave = n_graves / n
  ) %>%
  filter(n > 100) %>%                       
  slice_max(proporcao_grave, n = 10) %>%    
  ggplot(aes(x = reorder(causa_acidente, proporcao_grave), 
             y = proporcao_grave)) +
  geom_col() +
  coord_flip() +
  labs(
    title = "Top 10 causas com maior proporção de acidentes graves",
    x = "Causa do acidente",
    y = "Proporção de acidentes graves"
  )

Análises por horário

Para realizar a análize de acidentes por período do dia, baseado nas horas dos acidentes, foi feito um mapeamento divdido entre Madrugada, Manhã, Tarde e Noite para melhor intuitividade analítica.

df <- df %>%
  mutate(
    hora = hour(horario),
    periodo_dia = case_when(
      hora >= 0 & hora < 6  ~ "Madrugada",
      hora >= 6 & hora < 12 ~ "Manhã",
      hora >= 12 & hora < 18 ~ "Tarde",
      hora >= 18 & hora <= 23 ~ "Noite",
      TRUE ~ NA_character_
    ),
    periodo_dia = factor(periodo_dia,
                         levels = c("Madrugada", "Manhã", "Tarde", "Noite"),
                         ordered = TRUE)
  )

É possível observar um padrão bastante claro de concentração dos acidentes ao longo do dia. O pico mais acentuado ocorre por volta das 18h, indicando que esse é o horário de maior densidade de ocorrências, o que coincide com o fim do expediente de trabalho e o início do “horário de pico” no trânsito, quando há maior volume de veículos nas rodovias e também maior cansaço dos condutores.

Em seguida, a tarde apresenta a segunda maior densidade, possivelmente relacionada ao alto fluxo de deslocamentos intermunicipais, transporte de cargas e viagens de média distância ao longo do dia. A manhã aparece na terceira posição, ainda com volume significativo de acidentes, especialmente associado ao deslocamento para o trabalho e atividades escolares, mas em menor intensidade que a tarde e a noite.

á a madrugada apresenta a menor densidade de acidentes, o que é esperado devido ao fluxo reduzido de veículos, embora esses acidentes costumem ser mais graves, geralmente associados a fatores como sono, álcool e excesso de velocidade. Esse padrão evidencia a forte relação entre acidentes e volume de tráfego, além de indicar horários prioritários para ações de fiscalização, campanhas de prevenção e reforço da segurança viária.

df %>%
  ggplot(aes(x = hora, fill = periodo_dia)) +
  geom_density(alpha = 0.4) +
  scale_x_continuous(breaks = seq(0, 23, 3)) +
  labs(
    title = "Densidade de acidentes por período do dia",
    x = "Hora do dia",
    y = "Densidade",
    fill = "Período"
  ) +
  theme_minimal()

Conclusão

Este trabalho teve como objetivo realizar uma análise exploratória completa dos acidentes rodoviários registrados pela Polícia Rodoviária Federal (PRF) no período de 2007 a 2023 em todo o território nacional. A pesquisa buscou compreender os padrões temporais, geográficos e causais dos acidentes, identificando fatores de risco críticos e propondo diretrizes para políticas públicas de segurança viária.

Para realizar essa análise, foram consolidados 17 anos de dados anuais em formato CSV, seguindo um rigoroso processo de limpeza e padronização. Os dados foram submetidos a tratamento que incluiu a extração automática do ano a partir dos nomes dos arquivos, padronização de colunas, conversão de tipos de dados e criação de novas variáveis analíticas, como a classificação de acidentes graves. O conjunto final permitiu análises multidimensionais com aproximadamente 2013757 registros.

As análises realizadas revelaram padrões consistentes e preocupantes. Temporalmente, identificou-se que os acidentes seguem ciclos bem definidos: a sexta-feira é o dia mais perigoso da semana, concentrando o maior número de ocorrências, seguida pelo sábado e domingo. O horário das 18h emergiu como o pico crítico diário, representando uma convergência perigosa de fatores como término do expediente, redução da luminosidade e cansaço acumulado.

Geograficamente, o ranking por Unidades Federativas trouxe surpresas significativas. Minas Gerais liderou o número de acidentes, superando estados mais populosos como São Paulo, que apareceu em sexto lugar. Santa Catarina e Paraná ocuparam a segunda e terceira posições, respectivamente, revelando a importância dos corredores logísticos do Sul do país. A análise regional mostrou que o Sudeste e Sul concentram aproximadamente 70% dos acidentes, refletindo tanto a densidade econômica quanto características geográficas desafiadoras.

Quanto às causas, os dados apontaram para a vulnerabilidade extrema dos pedestres, com causas relacionadas a essa categoria dominando o ranking de periculosidade. O comportamento do pedestre “na pista” foi a causa mais frequente, seguida por “entrada inoportuna” e “cruzamento fora da faixa”. Entre os veículos, as colisões frontais e saídas de pista mostraram as maiores proporções de gravidade.

As rodovias federais mais perigosas seguiram um padrão claro: BR-101 e BR-116 lideram o ranking, confirmando sua importância como eixos vitais da economia nacional e, consequentemente, pontos críticos de acidentes. Rodovias como a BR-381 (Fernão Dias) e BR-040 completam o topo da lista, todas caracterizadas por intenso tráfego de carga, trechos sinuosos e condições climáticas adversas.

A análise longitudinal revelou uma tendência preocupante: após um pico entre 2010-2012, houve redução significativa nos números absolutos a partir de 2015, mas essa queda parece ter se estabilizado em patamares ainda elevados. O ano de 2020, marcado pela pandemia, apresentou os menores índices, porém com recuperação parcial nos anos seguintes.

Para responder com maior precisão às variações observadas e aprofundar a compreensão dos fatores subjacentes, serão necessárias etapas futuras de pesquisa. Uma análise mais detalhada dos dados meteorológicos correlacionados com os acidentes poderia revelar padrões sazonais críticos. Também seria valioso investigar a relação entre investimentos em infraestrutura rodoviária e redução de acidentes em períodos específicos.

Além disso, pretende-se expandir a análise para incluir dados de rodovias estaduais, que podem revelar realidades diferentes das capturadas apenas pelas federais. A inclusão de variáveis socioeconômicas regionais poderia ajudar a explicar variações entre estados com características demográficas similares. Por fim, uma análise comparativa com políticas de segurança viária implementadas em outros países poderia oferecer insights valiosos para o contexto brasileiro.

Este trabalho demonstra que a segurança viária no Brasil é um desafio complexo e multifatorial, exigindo abordagens diferenciadas por região, período do dia e tipo de via. Os padrões identificados oferecem uma base sólida para políticas públicas mais eficientes, que devem priorizar intervenções nos horários, locais e condições de maior risco. A continuidade desta pesquisa é essencial para monitorar a efetividade das medidas implementadas e adaptar estratégias conforme a evolução do cenário nacional de segurança viária.