Análise de Padrões de Velocidade nas Vias do Recife

Introdução

Contexto e Relevância

O Problema da Segurança Viária em Recife

O excesso de velocidade é um dos principais fatores contribuintes para acidentes de trânsito fatais no Brasil. Em áreas urbanas como o Recife, a fiscalização e o monitoramento das velocidades nas vias públicas representam uma importante estratégia para aumentar a segurança viária e reduzir a mortalidade no trânsito.

Este projeto analisa os dados coletados por equipamentos de fiscalização (lombadas eletrônicas e fotossensores) nas vias do Recife durante o ano de 2020, buscando compreender padrões de velocidade, identificar pontos críticos e oferecer insights para políticas públicas de segurança viária.

Por que a análise de velocidade importa?

  • O Brasil registra aproximadamente 30.000 mortes anuais em acidentes de trânsito
  • Velocidade excessiva está presente em 30% dos acidentes com vítimas fatais
  • A probabilidade de morte de pedestres sobe de 20% a 85% quando a velocidade aumenta de 30km/h para 50km/h
  • A fiscalização eletrônica reduz em até 35% os acidentes em pontos críticos

Dados e Metodologia

Fonte e Natureza dos Dados

Este projeto utiliza dados públicos disponibilizados pela Prefeitura do Recife através do Portal de Dados Abertos (http://dados.recife.pe.gov.br/). O conjunto específico analisado é “Velocidade das Vias - Quantitativo por Velocidade Média - 2020”, que registra:

  • Contagens de veículos em diferentes faixas de velocidade
  • Registros em intervalos de 15 minutos
  • Dados de equipamentos distribuídos pela cidade
  • Informações da localização geográfica dos equipamentos
12 meses

Abordagem Metodológica

Nossa análise seguirá estas etapas principais:

  1. Importação e limpeza dos dados - Unificação dos arquivos mensais, tratamento de inconsistências e padronização
  2. Georreferenciamento - Visualização espacial dos equipamentos de monitoramento
  3. Análise temporal - Identificação de padrões por hora do dia, dia da semana e sazonalidade
  4. Análise de conformidade - Avaliação do percentual de veículos acima do limite de velocidade permitido
  5. Identificação de hotspots - Locais com maior incidência de infrações graves

Valor para Gestores de Trânsito

Benefícios para Tomadores de Decisão

Esta análise fornece insights valiosos para:

  • Gestores de trânsito - Identificação de pontos críticos para intensificação da fiscalização
  • Planejadores urbanos - Evidências para intervenções de engenharia de tráfego e traffic calming
  • Formuladores de políticas - Dados para avaliar a eficácia das medidas de controle de velocidade
  • Campanhas educativas - Direcionamento para horários e locais com maior incidência de infrações

Pacotes e Preparação dos Dados

Pacotes Utilizados

Bibliotecas Necessárias para a Análise

# Manipulação de dados
library(tidyverse)    # Conjunto de pacotes para manipulação de dados
library(readr)        # Importação eficiente de dados
library(lubridate)    # Manipulação de datas
library(janitor)      # Limpeza de nomes de variáveis

# Visualização
library(ggplot2)      # Criação de gráficos
library(plotly)       # Gráficos interativos
library(leaflet)      # Mapas interativos
library(viridis)      # Paletas de cores acessíveis
library(scales)       # Formatação de escalas em gráficos

# Apresentação
library(knitr)        # Geração de relatórios dinâmicos
library(kableExtra)   # Tabelas formatadas
library(DT)           # Tabelas interativas
library(flexdashboard) # Layout de dashboard

Propósito dos pacotes principais:

  • tidyverse: Conjunto de pacotes que compartilham uma filosofia de design comum para manipulação de dados em R.
  • ggplot2: Sistema de visualização baseado na “gramática dos gráficos” para criar gráficos estatísticos.
  • leaflet: Cria mapas interativos integrando a biblioteca JavaScript Leaflet.
  • lubridate: Simplifica trabalho com datas e horas, essencial para nossa análise temporal.
  • plotly: Permite criar visualizações interativas que facilitam a exploração dos dados.
  • flexdashboard: Fornece o framework para estruturar este relatório em formato de dashboard.

Importação dos Dados

Carregamento e Amostragem Inicial

# Importando arquivo de localização dos equipamentos
lombadas_fotossensores <- read_delim("lombadas-fotossensores.csv", 
                                    delim = ";", 
                                    locale = locale(decimal_mark = ","),
                                    col_types = cols(
                                      equipamento = col_character(),
                                      tipo = col_character(),
                                      logradouro = col_character(),
                                      velocidade_via = col_character(),
                                      latitude = col_double(),
                                      longitude = col_double()
                                    ))

# Função para importar dados mensais de fotossensores
importar_fotossensores_mensais <- function(mes) {
  arquivo <- sprintf("recife-fotossensores-20-%s.csv", mes)
  dados <- read_delim(arquivo, 
                      delim = ";", 
                      locale = locale(decimal_mark = ","),
                      col_types = cols(
                        mes = col_integer(),
                        equipamento = col_character(),
                        faixa = col_integer(),
                        data = col_date(format = ""),
                        hora = col_integer(),
                        minutos_intervalo = col_character(),
                        .default = col_integer()
                      ))
  return(dados)
}

# Função para importar dados mensais de lombadas
importar_lombadas_mensais <- function(mes) {
  arquivo <- sprintf("%slomb2020.csv", mes)
  dados <- read_delim(arquivo, 
                      delim = ";", 
                      locale = locale(decimal_mark = ","),
                      col_types = cols(
                        ano = col_integer(),
                        mes = col_character(),
                        equipamento = col_character(),
                        faixa = col_integer(),
                        data = col_date(format = ""),
                        hora = col_integer(),
                        minutos_intervalo = col_character(),
                        .default = col_integer()
                      ))
  return(dados)
}

# Importando o mês de janeiro para demonstração
fotossensores_janeiro <- importar_fotossensores_mensais("janeiro")
lombadas_janeiro <- importar_lombadas_mensais("janeiro")

# Exibindo primeiras linhas dos dados
cat("Equipamentos de monitoramento:\n")
## Equipamentos de monitoramento:
head(lombadas_fotossensores, 3) %>% kable()
equipamento tipo logradouro velocidade_via latitude longitude
5941 Lombada AV. MAL. MASCARENHAS DE MORAES, EM FRENTE AEROPORTO - SENT. PRAZERES 60 km/h NA NA
5942 Lombada AV. MAL. MASCARENHAS DE MORAES, EM FRENTE AEROPORTO - SENT. CENTRO 60 km/h NA NA
5943 Lombada AV. BOA VIAGEM - TERCEIRO JARDIM 60 km/h NA NA
cat("\nDados de fotossensores (Janeiro 2020):\n")
## 
## Dados de fotossensores (Janeiro 2020):
head(fotossensores_janeiro, 3) %>% kable()
mes equipamento faixa data hora minutos_intervalo qtd_0a10km qtd_11a20km qtd_21a30km qtd_31a40km qtd_41a50km qtd_51a60km qtd_61a70km qtd_71a80km qtd_81a90km qtd_91a100km qtd_acimade100km
1 FS002REC 1 2020-01-01 0 0-15 1 2 8 2 0 0 0 0 0 0 0
1 FS002REC 1 2020-01-01 0 16-30 0 8 8 3 0 0 0 0 0 0 0
1 FS002REC 1 2020-01-01 0 31-45 0 12 21 2 0 0 0 0 0 0 0
cat("\nDados de lombadas (Janeiro 2020):\n")
## 
## Dados de lombadas (Janeiro 2020):
head(lombadas_janeiro, 3) %>% kable()
ano mes equipamento faixa data hora minutos_intervalo qtd_0a10km qtd_11a20km qtd_21a30km qtd_31a40km qtd_41a50km qtd_51a60km qtd_61a70km qtd_71a80km qtd_81a90km qtd_91a100km qtd_acimade100km
2020 01 1 1 2020-01-01 10 10:00-10:15 0 2 1 7 25 13 0 0 0 0 0
2020 01 1 1 2020-01-01 10 10:15-10:30 0 0 0 6 9 8 0 0 0 0 0
2020 01 1 1 2020-01-01 10 10:30-10:45 1 0 2 9 13 7 0 0 0 0 0

Limpeza e Transformação

Padronização e Estruturação dos Dados

# Corrigindo formato da velocidade na via
lombadas_fotossensores <- lombadas_fotossensores %>%
  mutate(
    # Extrair apenas o número da velocidade (removendo "km/h")
    velocidade_via = as.integer(str_extract(velocidade_via, "\\d+"))
  )

# Unificando os dados de fotossensores e lombadas (janeiro como exemplo)
lombadas_janeiro <- lombadas_janeiro %>%
  # Garantir que mes seja numérico para consistência
  mutate(mes = as.integer(mes))

# Função para transformar dados em formato longo (melhor para análise)
transformar_para_longo <- function(df) {
  df %>%
    pivot_longer(
      cols = starts_with("qtd_"),
      names_to = "faixa_velocidade",
      values_to = "quantidade_veiculos"
    ) %>%
    # Extrair limites de velocidade da faixa
    mutate(
      faixa_velocidade = case_when(
        faixa_velocidade == "qtd_0a10km" ~ "0-10",
        faixa_velocidade == "qtd_11a20km" ~ "11-20",
        faixa_velocidade == "qtd_21a30km" ~ "21-30",
        faixa_velocidade == "qtd_31a40km" ~ "31-40",
        faixa_velocidade == "qtd_41a50km" ~ "41-50",
        faixa_velocidade == "qtd_51a60km" ~ "51-60",
        faixa_velocidade == "qtd_61a70km" ~ "61-70",
        faixa_velocidade == "qtd_71a80km" ~ "71-80",
        faixa_velocidade == "qtd_81a90km" ~ "81-90",
        faixa_velocidade == "qtd_91a100km" ~ "91-100",
        faixa_velocidade == "qtd_acimade100km" ~ ">100",
        TRUE ~ faixa_velocidade
      ),
      # Criar coluna com velocidade média da faixa (para cálculos)
      velocidade_media = case_when(
        faixa_velocidade == "0-10" ~ 5,
        faixa_velocidade == "11-20" ~ 15,
        faixa_velocidade == "21-30" ~ 25,
        faixa_velocidade == "31-40" ~ 35,
        faixa_velocidade == "41-50" ~ 45,
        faixa_velocidade == "51-60" ~ 55,
        faixa_velocidade == "61-70" ~ 65,
        faixa_velocidade == "71-80" ~ 75,
        faixa_velocidade == "81-90" ~ 85,
        faixa_velocidade == "91-100" ~ 95,
        faixa_velocidade == ">100" ~ 110,
        TRUE ~ NA_real_
      )
    )
}

# Transformando os dados para formato longo
fotossensores_janeiro_longo <- transformar_para_longo(fotossensores_janeiro)
lombadas_janeiro_longo <- transformar_para_longo(lombadas_janeiro)

# Corrigindo a função adicionar_periodo
adicionar_periodo <- function(df) {
  df %>%
    mutate(
      periodo = case_when(
        hora >= 0 & hora < 6 ~ "Madrugada",
        hora >= 6 & hora < 12 ~ "Manhã",
        hora >= 12 & hora < 18 ~ "Tarde",
        hora >= 18 & hora <= 23 ~ "Noite",
        TRUE ~ "Desconhecido"
      ),
      # Extrair dia da semana usando abordagem mais segura
      dia_semana = factor(
        wday(data),
        levels = 1:7,
        labels = c("Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado")
      )
    )
}

fotossensores_janeiro_longo <- adicionar_periodo(fotossensores_janeiro_longo)
lombadas_janeiro_longo <- adicionar_periodo(lombadas_janeiro_longo)

# Exibindo amostra dos dados transformados
cat("Dados de fotossensores em formato longo:\n")
## Dados de fotossensores em formato longo:
fotossensores_janeiro_longo %>% 
  head(5) %>% 
  select(data, hora, periodo, dia_semana, equipamento, faixa_velocidade, quantidade_veiculos) %>%
  kable()
data hora periodo dia_semana equipamento faixa_velocidade quantidade_veiculos
2020-01-01 0 Madrugada Quarta FS002REC 0-10 1
2020-01-01 0 Madrugada Quarta FS002REC 11-20 2
2020-01-01 0 Madrugada Quarta FS002REC 21-30 8
2020-01-01 0 Madrugada Quarta FS002REC 31-40 2
2020-01-01 0 Madrugada Quarta FS002REC 41-50 0

Junção com Localização

Enriquecendo os Dados com Informações Geográficas

# Corrigindo os identificadores de equipamentos nos dados de fotossensores
fotossensores_janeiro_longo <- fotossensores_janeiro_longo %>%
  mutate(
    # Remove o sufixo "REC" dos identificadores de equipamentos
    equipamento = str_replace(equipamento, "REC$", "")
  )

# Juntando dados de velocidade com localização dos equipamentos
fotossensores_janeiro_geo <- fotossensores_janeiro_longo %>%
  left_join(
    lombadas_fotossensores %>% select(equipamento, tipo, logradouro, velocidade_via, latitude, longitude),
    by = "equipamento"
  )

# Verificando quantidade de dados por tipo de equipamento
contagem_por_tipo <- fotossensores_janeiro_geo %>%
  filter(!is.na(tipo)) %>%
  group_by(tipo) %>%
  summarise(
    registros = n(),
    equipamentos_distintos = n_distinct(equipamento)
  )

# Calcular velocidade excedente
fotossensores_janeiro_geo <- fotossensores_janeiro_geo %>%
  mutate(
    # Determinar se está acima da velocidade permitida
    acima_limite = ifelse(!is.na(velocidade_via) & velocidade_media > velocidade_via, TRUE, FALSE),
    # Calcular o quanto excede (em km/h e percentual)
    excesso_kmh = ifelse(acima_limite, velocidade_media - velocidade_via, 0),
    excesso_percentual = ifelse(!is.na(velocidade_via), (velocidade_media / velocidade_via - 1) * 100, NA)
  )

# Resumo dos dados por tipo de equipamento
kable(contagem_por_tipo, caption = "Quantidade de registros por tipo de equipamento")
Quantidade de registros por tipo de equipamento
tipo registros equipamentos_distintos
Fotossensor 2482095 30
# Exibir dados com informações de localização
fotossensores_janeiro_geo %>%
  filter(!is.na(tipo)) %>%
  select(data, equipamento, tipo, logradouro, velocidade_via, faixa_velocidade, 
         velocidade_media, quantidade_veiculos, acima_limite) %>%
  head(5) %>%
  kable()
data equipamento tipo logradouro velocidade_via faixa_velocidade velocidade_media quantidade_veiculos acima_limite
2020-01-01 FS002 Fotossensor RUA MADRE DE DEUS, SEMAFORO 020. 30 0-10 5 1 FALSE
2020-01-01 FS002 Fotossensor RUA MADRE DE DEUS, SEMAFORO 020. 30 11-20 15 2 FALSE
2020-01-01 FS002 Fotossensor RUA MADRE DE DEUS, SEMAFORO 020. 30 21-30 25 8 FALSE
2020-01-01 FS002 Fotossensor RUA MADRE DE DEUS, SEMAFORO 020. 30 31-40 35 2 TRUE
2020-01-01 FS002 Fotossensor RUA MADRE DE DEUS, SEMAFORO 020. 30 41-50 45 0 TRUE
# Exibir dados com informações de localização (sem filtrar por tipo)
fotossensores_janeiro_geo %>%
  select(data, equipamento, tipo, logradouro, velocidade_via, faixa_velocidade, 
         velocidade_media, quantidade_veiculos, acima_limite) %>%
  head(5) %>%
  kable()
data equipamento tipo logradouro velocidade_via faixa_velocidade velocidade_media quantidade_veiculos acima_limite
2020-01-01 FS002 Fotossensor RUA MADRE DE DEUS, SEMAFORO 020. 30 0-10 5 1 FALSE
2020-01-01 FS002 Fotossensor RUA MADRE DE DEUS, SEMAFORO 020. 30 11-20 15 2 FALSE
2020-01-01 FS002 Fotossensor RUA MADRE DE DEUS, SEMAFORO 020. 30 21-30 25 8 FALSE
2020-01-01 FS002 Fotossensor RUA MADRE DE DEUS, SEMAFORO 020. 30 31-40 35 2 TRUE
2020-01-01 FS002 Fotossensor RUA MADRE DE DEUS, SEMAFORO 020. 30 41-50 45 0 TRUE

Resumo dos Dados

Estatísticas Descritivas e Integridade dos Dados

# Calculando estatísticas gerais
total_registros <- nrow(fotossensores_janeiro_longo)
total_veiculos <- sum(fotossensores_janeiro_longo$quantidade_veiculos, na.rm = TRUE)
total_equipamentos <- n_distinct(fotossensores_janeiro_longo$equipamento)
dias_distintos <- n_distinct(fotossensores_janeiro_longo$data)

# Valores ausentes
ausentes <- fotossensores_janeiro_geo %>%
  summarise(across(everything(), ~sum(is.na(.)))) %>%
  pivot_longer(everything(), names_to = "variavel", values_to = "valores_ausentes") %>%
  filter(valores_ausentes > 0)

# Distribuição por faixa de velocidade
dist_velocidade <- fotossensores_janeiro_longo %>%
  group_by(faixa_velocidade) %>%
  summarise(
    total_veiculos = sum(quantidade_veiculos, na.rm = TRUE),
    pct = total_veiculos / sum(fotossensores_janeiro_longo$quantidade_veiculos, na.rm = TRUE) * 100
  ) %>%
  arrange(faixa_velocidade)

# Estatísticas gerais em valueboxes
valueBox(formatC(total_veiculos, format="d", big.mark=","), 
         "Veículos registrados (Jan/2020)", 
         icon = "fa-car", 
         color = "primary")
16,259,554
valueBox(total_equipamentos, 
         "Equipamentos monitorados", 
         icon = "fa-camera", 
         color = "info")
30
valueBox(dias_distintos, 
         "Dias com registros", 
         icon = "fa-calendar-check", 
         color = "success")
31
# Exibir distribuição de velocidades
dist_velocidade %>%
  kable(col.names = c("Faixa de Velocidade (km/h)", "Total de Veículos", "Percentual (%)"),
        caption = "Distribuição de veículos por faixa de velocidade") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
Distribuição de veículos por faixa de velocidade
Faixa de Velocidade (km/h) Total de Veículos Percentual (%)
0-10 94323 0.5801082
11-20 2100363 12.9177159
21-30 3673784 22.5946173
31-40 4678025 28.7709306
41-50 4358145 26.8035950
51-60 1225826 7.5391121
61-70 121662 0.7482493
71-80 5274 0.0324363
81-90 1076 0.0066176
91-100 319 0.0019619
>100 757 0.0046557
# Gráfico de distribuição
ggplot(dist_velocidade, aes(x = faixa_velocidade, y = total_veiculos)) +
  geom_col(fill = "steelblue") +
  labs(title = "Distribuição de Veículos por Faixa de Velocidade",
       x = "Faixa de Velocidade (km/h)",
       y = "Quantidade de Veículos") +
  theme_minimal() +
  scale_y_continuous(labels = scales::comma)