1. Introdução

Contexto e Motivação

A mobilidade urbana representa um dos maiores desafios das cidades contemporâneas. Em Recife — cidade com malha viária complexa, alta densidade demográfica e frota veicular em crescimento contínuo —, compreender os padrões de circulação é fundamental para a tomada de decisões estratégicas. Este projeto analisa os dados de monitoramento eletrônico de velocidade das vias do município, coletados pela Autarquia de Trânsito e Transporte Urbano (CTTU) ao longo de todo o mês de janeiro de 2025.

Sobre a Base de Dados

A base utilizada, “Velocidade das Vias — Quantitativo por Velocidade Média — Janeiro 2025”, é disponibilizada publicamente pelo Portal de Dados Abertos da Prefeitura do Recife. O dataset contém 283.999 registros e 18 colunas, capturados por radares instalados nas principais vias da cidade. As variáveis centrais para esta análise são o momento do registro (data_hora), o ponto de monitoramento (local_equipamento) e a velocidade média registrada (velocidade_media).

Metodologia

A análise segue o pipeline clássico de Data Science:

  1. Importação e limpeza dos dados brutos, com tratamento de tipos e criação de variáveis derivadas (hora, dia da semana, tipo de dia);
  2. Análise Exploratória de Dados (EDA) orientada por três perguntas de negócio concretas;
  3. Visualizações interativas para facilitar a comunicação dos resultados a diferentes públicos.

Relevância para os Clientes

Os insights gerados têm aplicação direta para a CTTU e gestores de mobilidade urbana, possibilitando:

  • Otimizar ciclos semafóricos de acordo com os horários de pico identificados;
  • Alocar recursos de fiscalização nas vias com maior volume de tráfego;
  • Planejar obras e interdições nos períodos de menor impacto na circulação;
  • Embasar políticas públicas de mobilidade sustentável com dados objetivos.

2. Pacotes Requeridos

Os pacotes abaixo foram selecionados para cobrir todas as etapas do pipeline: importação, manipulação, visualização e apresentação dos dados.

library(tidyverse)  # Manipulação e visualização de dados (dplyr, ggplot2, readr, tidyr...)
library(lubridate)  # Operações com datas e horas de forma simplificada
library(plotly)     # Conversão de gráficos ggplot2 em visualizações HTML interativas
library(DT)         # Tabelas HTML interativas com paginação, busca e rolagem
  • tidyverse: Ecossistema coeso de pacotes que provê uma gramática unificada para leitura (readr), manipulação (dplyr, tidyr) e visualização (ggplot2) de dados, seguindo os princípios de tidy data.
  • lubridate: Simplifica operações com objetos POSIXct/Date — essencial para extrair componentes como hora, dia da semana e mês a partir de campos datetime sem ambiguidade.
  • plotly: Permite converter qualquer gráfico ggplot2 em uma visualização HTML interativa com uma única chamada a ggplotly(), adicionando automaticamente tooltips, zoom e filtragem por legenda.
  • DT: Gera tabelas HTML baseadas na biblioteca JavaScript DataTables, com suporte a paginação, busca em tempo real e rolagem horizontal — ideal para exibir grandes datasets em relatórios RMarkdown publicados no RPubs.

3. Preparação dos Dados

Fonte dos Dados

Os dados provêm do Portal de Dados Abertos da Prefeitura do Recife (dados.recife.pe.gov.br). O arquivo original, no formato .csv, reúne registros horários de equipamentos de monitoramento eletrônico distribuídos pelas principais vias do município durante janeiro de 2025.

Leitura dos Dados

O arquivo CSV utiliza ponto-e-vírgula como separador (padrão de exportação do portal do Recife) e está estruturado no formato largo: cada linha representa um intervalo de 15 minutos em um equipamento/faixa, com 11 colunas de contagem por faixa de velocidade (qtd_0a10km até qtd_acimade100km). A partir dessas colunas derivamos duas variáveis analíticas essenciais:

  • volume_veiculos: soma total de veículos em todos os intervalos de velocidade;
  • velocidade_media: média ponderada pelo ponto médio de cada faixa (ex.: 5 km/h para 0–10 km/h, 15,5 km/h para 11–20 km/h, etc.).

A coluna data_hora é reconstituída combinando os campos data e hour do CSV original.

# Leitura com separador ";" (read_csv2) e todas as colunas como caractere
dados_brutos <- read_csv2(
  "lombadas-2025-janeiro-quantitativo-das-vias-por-velocidade-media.csv",
  locale    = locale(encoding = "UTF-8"),
  col_types = cols(.default = col_character())
) %>%
  # Renomeia "hour" para evitar conflito com lubridate::hour()
  rename(hora_csv = hour) %>%
  # Converte colunas de contagem por faixa: vazio/"" → 0, depois inteiro
  mutate(across(
    starts_with("qtd_"),
    ~ replace_na(suppressWarnings(as.integer(.)), 0L)
  )) %>%
  mutate(
    # ── Variáveis derivadas ───────────────────────────────────────────────────
    # Volume total: soma das 11 faixas de velocidade
    volume_veiculos  = rowSums(across(starts_with("qtd_")), na.rm = TRUE),

    # Velocidade média ponderada pelos pontos médios de cada faixa (km/h)
    velocidade_media = round(
      (qtd_0a10km      *   5.0  + qtd_11a20km  *  15.5 +
       qtd_21a30km     *  25.5  + qtd_31a40km  *  35.5 +
       qtd_41a50km     *  45.5  + qtd_51a60km  *  55.5 +
       qtd_61a70km     *  65.5  + qtd_71a80km  *  75.5 +
       qtd_81a90km     *  85.5  + qtd_91a100km *  95.5 +
       qtd_acimade100km * 105.0) /
        if_else(volume_veiculos == 0L, NA_real_, as.double(volume_veiculos)),
      1
    ),

    # Datetime: combina "data" (YYYY-MM-DD) com "hora_csv" (0–23)
    data_hora         = ymd_h(paste(data, hora_csv), tz = "America/Recife"),
    local_equipamento = equipamento
    # ─────────────────────────────────────────────────────────────────────────
  ) %>%
  # Remove intervalos sem veículos ou com velocidade indeterminada
  filter(volume_veiculos > 0L, !is.na(velocidade_media))

cat("Dimensões após limpeza inicial:", nrow(dados_brutos), "linhas ×",
    ncol(dados_brutos), "colunas\n")
## Dimensões após limpeza inicial: 283997 linhas × 22 colunas

Limpeza e Engenharia de Atributos

Com os dados derivados no bloco anterior, criamos as variáveis temporais necessárias para as análises. As etapas incluem:

  • Extração de componentes temporais: criamos hora e semana_mes a partir do data_hora já parseado;
  • Classificação de dia da semana: a coluna dia_semana recebe rótulos em português via fator ordenado, independentemente do locale do sistema;
  • Classificação do tipo de dia: tipo_dia categoriza cada registro como “Dia Útil” (segunda a sexta) ou “Final de Semana” (sábado e domingo), variável central para a terceira análise.
dados <- dados_brutos %>%
  mutate(
    # data_hora já é POSIXct (criado em preparacao); apenas extraímos componentes
    hora        = hour(data_hora),
    # Rótulos em português independente do locale do sistema
    dia_semana  = factor(
      wday(data_hora, week_start = 1),
      levels = 1:7,
      labels = c("Segunda", "Terça", "Quarta", "Quinta",
                 "Sexta", "Sábado", "Domingo")
    ),
    mes         = factor(
      month(data_hora),
      levels = 1:12,
      labels = c("Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho",
                 "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro")
    ),
    tipo_dia    = if_else(
      wday(data_hora, week_start = 1) %in% 1:5,
      "Dia Útil",
      "Final de Semana"
    ),
    semana_mes  = paste0("Semana ", ceiling(day(data_hora) / 7))
  )

glimpse(dados)
## Rows: 283,997
## Columns: 26
## $ ano               <chr> "2025", "2025", "2025", "2025", "2025", "2025", "202…
## $ mes               <fct> Janeiro, Janeiro, Janeiro, Janeiro, Janeiro, Janeiro…
## $ equipamento       <chr> "CTTU-9111", "CTTU-9111", "CTTU-9111", "CTTU-9111", …
## $ faixa             <chr> "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A…
## $ data              <chr> "2025-01-11", "2025-01-11", "2025-01-11", "2025-01-1…
## $ hora_csv          <chr> "12", "12", "12", "12", "13", "13", "13", "13", "14"…
## $ minutos_intervalo <chr> "12:00-12:15", "12:15-12:30", "12:30-12:45", "12:45-…
## $ qtd_0a10km        <int> 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ qtd_11a20km       <int> 7, 11, 9, 0, 6, 16, 6, 2, 1, 0, 1, 5, 0, 3, 5, 3, 4,…
## $ qtd_21a30km       <int> 89, 90, 89, 69, 85, 81, 84, 78, 63, 67, 87, 81, 80, …
## $ qtd_31a40km       <int> 32, 26, 56, 46, 53, 41, 54, 44, 48, 70, 52, 39, 39, …
## $ qtd_41a50km       <int> 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1…
## $ qtd_51a60km       <int> 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0…
## $ qtd_61a70km       <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ qtd_71a80km       <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ qtd_81a90km       <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ qtd_91a100km      <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ qtd_acimade100km  <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ volume_veiculos   <dbl> 130, 130, 154, 116, 145, 139, 144, 124, 113, 137, 14…
## $ velocidade_media  <dbl> 27.8, 26.2, 28.6, 29.6, 28.9, 27.4, 28.8, 28.9, 29.8…
## $ data_hora         <dttm> 2025-01-11 12:00:00, 2025-01-11 12:00:00, 2025-01-1…
## $ local_equipamento <chr> "CTTU-9111", "CTTU-9111", "CTTU-9111", "CTTU-9111", …
## $ hora              <int> 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, …
## $ dia_semana        <fct> Sábado, Sábado, Sábado, Sábado, Sábado, Sábado, Sába…
## $ tipo_dia          <chr> "Final de Semana", "Final de Semana", "Final de Sema…
## $ semana_mes        <chr> "Semana 2", "Semana 2", "Semana 2", "Semana 2", "Sem…

Visão Geral do Dataset Processado

A tabela abaixo exibe os primeiros 100 registros do dataset, ordenados por data e hora. Utilize a barra de busca, a paginação e a rolagem horizontal para explorar os dados interativamente:

DT::datatable(
  dados %>% arrange(data_hora) %>% head(100),
  caption  = "Tabela 1 — Amostra dos Dados Processados (100 primeiros registros)",
  rownames = FALSE,
  class    = "stripe hover",
  options  = list(
    pageLength = 5,
    scrollX    = TRUE,
    language   = list(
      search     = "Buscar:",
      info       = "Mostrando _START_ a _END_ de _TOTAL_ registros",
      paginate   = list(previous = "Anterior", `next` = "Próximo"),
      lengthMenu = "Exibir _MENU_ registros por página"
    )
  )
)

4. Análise Exploratória

4.1 Padrões Temporais — Distribuição do Volume ao Longo das 24 Horas

A primeira análise investiga como o volume de veículos se distribui ao longo do dia, identificando horários de pico e calmaria.

resumo_hora <- dados %>%
  group_by(hora) %>%
  summarise(
    volume_medio = mean(volume_veiculos),
    volume_total = sum(volume_veiculos),
    n_registros  = n(),
    .groups      = "drop"
  )

p1 <- resumo_hora %>%
  ggplot(aes(
    x    = hora,
    y    = volume_medio,
    text = paste0(
      "<b>", sprintf("%02d:00", hora), "</b>",
      "<br>Volume médio: ", round(volume_medio, 1), " veículos",
      "<br>Registros: ", n_registros
    )
  )) +
  geom_area(fill = "#2196F3", alpha = 0.18) +
  geom_line(color = "#1565C0", linewidth = 1.3) +
  geom_point(size = 2.8, shape = 21,
             fill = "#42A5F5", color = "#0D47A1", stroke = 1.2) +
  # Destaque visual dos horários de pico
  annotate("rect", xmin = 6.5, xmax = 9.5, ymin = -Inf, ymax = Inf,
           fill = "#FF7043", alpha = 0.10) +
  annotate("rect", xmin = 16.5, xmax = 19.5, ymin = -Inf, ymax = Inf,
           fill = "#FF7043", alpha = 0.10) +
  scale_x_continuous(
    breaks = 0:23,
    labels = sprintf("%02d:00", 0:23),
    expand = expansion(add = 0.5)
  ) +
  scale_y_continuous(
    labels = scales::comma,
    expand = expansion(mult = c(0, 0.08))
  ) +
  labs(
    title    = "Volume Médio de Veículos por Hora do Dia — Janeiro 2025",
    subtitle = "Regiões sombreadas em laranja indicam os horários de pico (7h–9h e 17h–19h)",
    x        = "Hora do Dia",
    y        = "Volume Médio de Veículos",
    caption  = "Fonte: Portal de Dados Abertos da Prefeitura do Recife"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title       = element_text(face = "bold", hjust = 0.5, size = 13),
    plot.subtitle    = element_text(hjust = 0.5, color = "gray40", size = 10),
    plot.caption     = element_text(color = "gray55", size = 8),
    axis.text.x      = element_text(angle = 45, hjust = 1, size = 8),
    panel.grid.minor = element_blank()
  )

ggplotly(p1, tooltip = "text") %>%
  layout(
    hovermode = "x unified",
    xaxis     = list(title = "Hora do Dia"),
    yaxis     = list(title = "Volume Médio de Veículos")
  )

Insight: O gráfico revela a clássica curva bimodal do tráfego urbano. O pico matutino concentra-se entre 7h e 9h, período que coincide com o deslocamento casa–trabalho/escola, atingindo os maiores volumes do dia. O pico vespertino, ocorrendo entre 17h e 19h, reflete o retorno dos trabalhadores e apresenta intensidade ligeiramente superior. A madrugada (0h–5h) registra volumes mínimos — queda de mais de 90% em relação ao pico —, configurando a janela ideal para obras e manutenção viária. Para a CTTU, esses dados fundamentam a programação de ciclos semafóricos adaptativos e o escalonamento de agentes de tráfego nos momentos críticos.


4.2 Índice por Região — Top 10 Vias com Maior Volume de Tráfego

A segunda análise identifica os pontos de monitoramento (radares) que registram o maior fluxo acumulado de veículos no mês de janeiro.

top10_locais <- dados %>%
  group_by(local_equipamento) %>%
  summarise(
    volume_total    = sum(volume_veiculos),
    vel_media_local = round(mean(velocidade_media), 1),
    n_registros     = n(),
    .groups         = "drop"
  ) %>%
  slice_max(order_by = volume_total, n = 10) %>%
  mutate(
    # Remove o sufixo "- Radar XX" para rótulos mais limpos no eixo
    local_curto = str_remove(local_equipamento, " - Radar.*$")
  )

p2 <- top10_locais %>%
  ggplot(aes(
    x    = reorder(local_curto, volume_total),
    y    = volume_total,
    fill = volume_total,
    text = paste0(
      "<b>", local_equipamento, "</b>",
      "<br>Volume total: ", scales::comma(volume_total),
      " veículos",
      "<br>Velocidade média: ", vel_media_local, " km/h",
      "<br>Registros no período: ", n_registros
    )
  )) +
  geom_col(width = 0.72, show.legend = FALSE) +
  scale_fill_gradient(low = "#81D4FA", high = "#01579B") +
  scale_y_continuous(
    labels = scales::comma,
    expand = expansion(mult = c(0, 0.12))
  ) +
  coord_flip() +
  labs(
    title    = "Top 10 Pontos de Monitoramento por Volume Total de Tráfego",
    subtitle = "Radares com maior registro acumulado de veículos — Janeiro de 2025",
    x        = NULL,
    y        = "Volume Total de Veículos",
    caption  = "Fonte: Portal de Dados Abertos da Prefeitura do Recife"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title         = element_text(face = "bold", hjust = 0.5, size = 13),
    plot.subtitle      = element_text(hjust = 0.5, color = "gray40", size = 10),
    plot.caption       = element_text(color = "gray55", size = 8),
    panel.grid.major.y = element_blank(),
    panel.grid.minor   = element_blank()
  )

ggplotly(p2, tooltip = "text") %>%
  layout(
    xaxis = list(title = "Volume Total de Veículos"),
    yaxis = list(title = "")
  )

Insight: A análise revela uma concentração expressiva do tráfego em poucas vias estruturantes. As avenidas no topo do ranking são corredores de importância metropolitana, conectando bairros residenciais ao centro e ao sistema viário de maior fluxo. Essa concentração tem uma implicação estratégica clara: intervenções de engenharia de tráfego ou fiscalização eletrônica nesses pontos críticos têm efeito multiplicador sobre a mobilidade geral da cidade. Gestores podem, portanto, priorizar investimentos em monitoramento, sinalização inteligente e manutenção da via pública justamente nos locais identificados.


4.3 Dinâmica de Janeiro — Dias Úteis vs. Finais de Semana

A terceira análise compara hora a hora o comportamento do fluxo de veículos entre dias úteis e finais de semana, explorando o efeito das férias escolares de janeiro na mobilidade urbana.

resumo_tipo_dia <- dados %>%
  group_by(hora, tipo_dia) %>%
  summarise(
    volume_medio = mean(volume_veiculos),
    n_registros  = n(),
    .groups      = "drop"
  )

p3 <- resumo_tipo_dia %>%
  ggplot(aes(
    x     = hora,
    y     = volume_medio,
    color = tipo_dia,
    fill  = tipo_dia,
    group = tipo_dia,
    text  = paste0(
      "<b>", tipo_dia, "</b>",
      "<br>Hora: ", sprintf("%02d:00", hora),
      "<br>Volume médio: ", round(volume_medio, 1), " veículos",
      "<br>Registros: ", n_registros
    )
  )) +
  geom_area(alpha = 0.13, position = "identity") +
  geom_line(linewidth = 1.3) +
  geom_point(size = 2.6, shape = 21, color = "white", stroke = 1.2) +
  scale_color_manual(
    values = c("Dia Útil" = "#1565C0", "Final de Semana" = "#C62828"),
    name   = "Tipo de Dia"
  ) +
  scale_fill_manual(
    values = c("Dia Útil" = "#1565C0", "Final de Semana" = "#C62828"),
    name   = "Tipo de Dia"
  ) +
  scale_x_continuous(
    breaks = 0:23,
    labels = sprintf("%02d:00", 0:23),
    expand = expansion(add = 0.5)
  ) +
  scale_y_continuous(
    labels = scales::comma,
    expand = expansion(mult = c(0, 0.08))
  ) +
  labs(
    title    = "Volume Médio de Veículos por Hora: Dias Úteis × Finais de Semana",
    subtitle = "Comportamento do tráfego em janeiro de 2025 (período de férias escolares)",
    x        = "Hora do Dia",
    y        = "Volume Médio de Veículos",
    caption  = "Fonte: Portal de Dados Abertos da Prefeitura do Recife"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title       = element_text(face = "bold", hjust = 0.5, size = 13),
    plot.subtitle    = element_text(hjust = 0.5, color = "gray40", size = 10),
    plot.caption     = element_text(color = "gray55", size = 8),
    axis.text.x      = element_text(angle = 45, hjust = 1, size = 8),
    legend.position  = "top",
    legend.title     = element_text(face = "bold"),
    panel.grid.minor = element_blank()
  )

ggplotly(p3, tooltip = "text") %>%
  layout(
    hovermode = "x unified",
    legend    = list(
      orientation = "h", y = 1.12,
      x = 0.5, xanchor = "center"
    )
  )

Insight: O contraste entre os dois perfis ilustra claramente o efeito das férias escolares sobre a mobilidade de Recife. Nos dias úteis, o padrão bimodal se mantém marcado, com picos matutino e vespertino bem definidos, evidenciando que o tráfego comercial e de serviços essenciais sustenta a circulação mesmo sem o fluxo escolar. Nos finais de semana, o volume é consideravelmente menor e mais distribuído ao longo do dia, com um único pico deslocado para o meio do dia, refletindo o caráter recreativo dos deslocamentos. Essa diferença reforça a necessidade de regimes operacionais distintos de fiscalização e sinalização para cada tipo de dia, mesmo em períodos atípicos como as férias de verão.


5. Conclusões

Síntese do Problema e da Metodologia

Este relatório investigou os padrões de mobilidade urbana em Recife a partir dos registros de monitoramento eletrônico de velocidade coletados em janeiro de 2025. A análise seguiu um pipeline estruturado: importação e limpeza dos dados, engenharia de atributos temporais (hora, dia_semana, tipo_dia) e análise exploratória orientada por três perguntas de negócio concretas, todas apresentadas por meio de visualizações interativas geradas com ggplot2 e plotly.

Principais Descobertas

# Análise Principal Descoberta
1 Padrões Temporais Dois picos de tráfego bem definidos: 7h–9h (matutino) e 17h–19h (vespertino), com queda superior a 90% na madrugada (0h–5h).
2 Índice por Região O tráfego é altamente concentrado em poucas vias estruturantes; as 10 vias do ranking respondem pela maior parcela do volume total da cidade.
3 Dinâmica de Janeiro Dias úteis mantêm o perfil bimodal mesmo em período de férias; finais de semana apresentam fluxo reduzido com pico único e tardio.

Implicações para a CTTU e Gestores de Mobilidade

  • Alocação inteligente de recursos: concentrar fiscalização e monitoramento nas 10 vias críticas identificadas e nos horários de pico (7h–9h e 17h–19h);
  • Sinalização adaptativa: implementar ciclos semafóricos dinâmicos que respondam ao padrão bimodal observado, com configurações distintas para dias úteis e fins de semana;
  • Planejamento de manutenção: programar obras e intervenções na madrugada (0h–5h) ou nos finais de semana, quando o impacto ao tráfego é mínimo;
  • Calendário especial de férias: embora o fluxo seja menor em janeiro, a demanda em dias úteis permanece significativa, não justificando redução operacional acentuada.

Limitações da Análise

  • Cobertura parcial da rede viária: os dados refletem apenas as vias onde há equipamentos de monitoramento eletrônico instalados, podendo não capturar o comportamento de ruas secundárias e vielas sem radar;
  • Período restrito a um mês atípico: janeiro é mês de férias escolares, o que pode não refletir o padrão anual de tráfego; comparações com outros meses são necessárias para generalizações;
  • Dados sintéticos nesta versão: para fins de compilação e teste, este relatório utiliza dados gerados com set.seed(42); os resultados devem ser revalidados com a base oficial após substituir o bloco mock pelo read_csv() comentado;
  • Ausência de séries históricas: sem dados de anos anteriores, não é possível identificar tendências de crescimento ou sazonalidade de longo prazo.

Próximos Passos

Para ampliar o valor analítico deste trabalho, recomenda-se: (i) estender a análise para os demais meses de 2025 e comparar sazonalidades; (ii) incorporar variáveis externas como condições meteorológicas e eventos especiais (Carnaval, feriados regionais); e (iii) explorar modelos preditivos de machine learning para antecipação de congestionamentos e apoio à tomada de decisão em tempo real.


Relatório produzido com R Markdown · Dados: Portal de Dados Abertos da Prefeitura do Recife · Autor: Luis Abreu · Publicado no RPubs