1. Introdução

O sistema de transporte público do Rio de Janeiro enfrenta desafios constantes de dimensionamento de frota e gestão de custos. Uma parcela significativa desse sistema é composta por passageiros que utilizam gratuidades (estudantes, idosos, pessoas com deficiência, entre outros). Compreender o comportamento desses usuários não é apenas uma questão de estatística, mas uma necessidade estratégica para a otimização de recursos públicos e melhoria da qualidade do serviço.

1.1 O Problema

A falta de visibilidade sobre os padrões exatos de deslocamento dos beneficiários de gratuidade pode levar a dois problemas principais: superlotação em horários escolares não previstos e ociosidade de frota em momentos de baixa demanda. Além disso, a distribuição desigual de passageiros gratuitos entre as operadoras impacta o equilíbrio econômico-financeiro do sistema.

1.2 Objetivos e Metodologia

Este projeto tem como objetivo principal analisar os dados de bilhetagem eletrônica de um dia típico (07/11/2025) para traçar o perfil do usuário de gratuidade. Utilizaremos técnicas de Ciência de Dados para limpar a base bruta, enriquecer as informações temporais e visualizar padrões de comportamento.

A análise busca responder: 1. Perfil: Qual a representatividade de cada grupo (estudantes vs. idosos) no total de gratuidades? 2. Geografia da Demanda: Quais polos educacionais (escolas) geram maior pressão no sistema? 3. Sazonalidade Diária: Como os picos de demanda variam entre os diferentes perfis? 4. Impacto Operacional: Qual a carga de gratuidades absorvida por cada empresa operadora?

1.3 Beneficiários da Análise

Os resultados aqui apresentados são de interesse direto da SETRAN (para fiscalização e planejamento), das empresas de ônibus (para alocação de veículos) e da sociedade civil (para transparência no uso de recursos).

2. Pacotes Requeridos

  • tidyverse: Manipulação de dados e visualização.
  • lubridate: Tratamento temporal.
  • janitor: Limpeza de nomes.
  • knitr/DT: Tabelas profissionais.
library(tidyverse)
library(lubridate)
library(janitor)
library(knitr)
library(DT)

3. Preparação dos Dados

3.1 Fonte dos Dados e Importação

Os dados utilizados neste projeto são públicos e foram obtidos no Portal de Dados Abertos do Rio de Janeiro. A base específica utilizada é a de “Gratuidade do Transporte de Mobilidade Urbana” (SGR).

# Importação dos dados
# Estamos usando encoding UTF-8 e separador ponto-e-vírgula, padrão do governo RJ
dados_brutos <- read_delim("TRANSACAO_GRATUIDADE_PUBLICO_2025_11_07.csv", 
                           delim = ";", 
                           locale = locale(encoding = "UTF-8"), 
                           show_col_types = FALSE)

# Limpeza e Padronização Inicial
dados_limpos <- dados_brutos %>%
  clean_names() %>% 
  # Converte vazios ("") em NA apenas nas colunas de texto para evitar erros com datas
  mutate(across(where(is.character), ~na_if(., ""))) %>% 
  # Remove colunas com dados sensíveis (hash, cartão) ou irrelevantes para análise macro
  select(-any_of(c("cartao_hash", "no_validador", "sindicato", "n_cartao")))

3.2 Engenharia de Atributos e Enriquecimento

Nesta etapa, realizamos o tratamento das datas, separamos o código da linha do nome e criamos categorias de horário. Também normalizamos os nomes das escolas para garantir que a contagem seja precisa.

# Tabela auxiliar para Join (Requisito: Mesclar dois conjuntos de dados)
categorias_gratuidade <- tibble(
  grupo_gratuidade = c("Estudante", "Idoso", "PCD", "Outros"),
  tipo_simplificado = c("Estudante", "Idoso", "PCD", "Outros")
)

dados_processados <- dados_limpos %>%
  # 1. Tratamento Inicial de Datas (Converter texto para objeto Data)
  mutate(
    data_obj = as_datetime(data_da_transacao), 
    data_processamento_obj = as_datetime(data_do_processamento)
  ) %>%
  
  # 2. Separar Linha em Código e Nome (Ex: "561L - CAXIAS" -> "561L" | "CAXIAS")
  separate(linha, into = c("codigo_linha", "nome_linha"), sep = " - ", extra = "merge", fill = "right") %>%
  
  # 3. Criação de Variáveis Temporais e Padronização
  mutate(
    hora = hour(data_obj),
    
    # Garantindo dia da semana em Português
    dia_num = wday(data_obj),
    dia_semana = case_when(
      dia_num == 1 ~ "Domingo",
      dia_num == 2 ~ "Segunda-feira",
      dia_num == 3 ~ "Terça-feira",
      dia_num == 4 ~ "Quarta-feira",
      dia_num == 5 ~ "Quinta-feira",
      dia_num == 6 ~ "Sexta-feira",
      dia_num == 7 ~ "Sábado"
    ),
    
    periodo_dia = case_when(
      hora >= 5 & hora < 12 ~ "Manhã",
      hora >= 12 & hora < 18 ~ "Tarde",
      hora >= 18 & hora < 24 ~ "Noite",
      TRUE ~ "Madrugada"
    ),
    
    # CONVERSÃO PARA FORMATO BRASILEIRO (Requisito: Característica não normalizada)
    data_transacao_br = format(data_obj, "%d/%m/%Y %H:%M:%S"),
    data_processamento_br = format(data_processamento_obj, "%d/%m/%Y"),
    
    # Padronização de Texto
    nome_linha = str_to_upper(nome_linha),
    operadora = str_to_title(operadora),
    escola = str_to_upper(escola) 
  ) %>%
  
  # 4. Criar chave para Join baseada na descrição da aplicação
  mutate(
    grupo_gratuidade = case_when(
      str_detect(descricao_da_aplicacao, "Estudante|Escola|Universit|Federal") ~ "Estudante",
      str_detect(descricao_da_aplicacao, "Idoso|Senior|Maringa") ~ "Idoso",
      str_detect(descricao_da_aplicacao, "Defic|Especial|Acompanhante") ~ "PCD",
      TRUE ~ "Outros"
    )
  ) %>%
  
  # 5. Realizar o Join com a tabela auxiliar
  left_join(categorias_gratuidade, by = "grupo_gratuidade")

# Exibir prévia dos dados tratados, mostrando a data formatada e colunas principais
head(dados_processados, 10) %>% 
  select(data_transacao_br, dia_semana, codigo_linha, nome_linha, tipo_simplificado, escola) %>%
  kable(col.names = c("Data (BR)", "Dia Semana", "Cód. Linha", "Nome Linha", "Tipo", "Escola"))
Data (BR) Dia Semana Cód. Linha Nome Linha Tipo Escola
06/11/2025 19:29:42 Quinta-feira 531MA NITEROI X J. ALCÂNTARA (V. DR MARCH) Idoso NA
06/11/2025 07:51:03 Quinta-feira 587 DK NITEROI-ARARUAMA V.SERRA URB Idoso NA
06/11/2025 16:47:29 Quinta-feira 531MA NITEROI X J. ALCÂNTARA (V. DR MARCH) Idoso NA
06/11/2025 06:01:14 Quinta-feira 587 DK NITEROI-ARARUAMA V.SERRA URB Idoso NA
06/11/2025 16:17:29 Quinta-feira 531MA NITEROI X J. ALCÂNTARA (V. DR MARCH) Idoso NA
06/11/2025 16:05:22 Quinta-feira 531MA NITEROI X J. ALCÂNTARA (V. DR MARCH) Idoso NA
06/11/2025 10:52:17 Quinta-feira 531MA NITEROI X J. ALCÂNTARA (V. DR MARCH) Idoso NA
06/11/2025 16:50:29 Quinta-feira 531MA NITEROI X J. ALCÂNTARA (V. DR MARCH) Idoso NA
06/11/2025 14:39:19 Quinta-feira 531MA NITEROI X J. ALCÂNTARA (V. DR MARCH) Idoso NA
06/11/2025 18:20:33 Quinta-feira 531MA NITEROI X J. ALCÂNTARA (V. DR MARCH) Idoso NA

3.3 Dicionário de Dados Final

Após as etapas de limpeza e transformação, o conjunto de dados final (dados_processados) ficou estruturado com as seguintes variáveis principais para análise:

  • data_transacao_br: Momento do uso formatado no padrão brasileiro (dd/mm/aaaa)
  • data_da_transacao: Momento exato do uso do cartão.
  • codigo_linha e nome_linha: Identificação da linha de ônibus (separados para melhor agrupamento).
  • escola: Nome da instituição de ensino (normalizado em maiúsculas).
  • tipo_simplificado: Categoria consolidada do beneficiário (Estudante, Idoso, PCD, etc), criada via junção (join) de dados.
  • hora e periodo_dia: Variáveis derivadas criadas para analisar o fluxo temporal (Manhã, Tarde, Noite).
  • operadora: Empresa responsável pelo transporte.

4. Análise Exploratória

4.1 Perfil Geral da Gratuidade

Qual grupo populacional mais utilizou o benefício na data analisada?

resumo_tipo <- dados_processados %>%
  filter(!is.na(tipo_simplificado)) %>%
  count(tipo_simplificado, sort = TRUE) %>%
  mutate(percentual = n / sum(n) * 100)

ggplot(resumo_tipo, aes(x = reorder(tipo_simplificado, n), y = n, fill = tipo_simplificado)) +
  geom_col(show.legend = FALSE) +
  geom_text(aes(label = paste0(round(percentual, 1), "%")), hjust = -0.1) +
  coord_flip() +
  labs(
    title = "Distribuição por Tipo de Benefício", 
    x = "", 
    y = "Quantidade de Transações"
  ) +
  theme_minimal()

Interpretação: A predominância de um grupo específico (como visto acima) indica onde as políticas públicas devem focar. Se os estudantes forem a maioria, a gestão deve estar atenta ao calendário escolar; se forem idosos, a acessibilidade física dos veículos torna-se prioridade crítica.

4.2 Mobilidade Estudantil: Ranking das Escolas

Abaixo listamos as 10 instituições de ensino que geraram o maior volume de viagens no dia. Esta informação é crucial para o planejamento de linhas escolares.

# Filtrando apenas estudantes com escola identificada no cadastro
top_escolas <- dados_processados %>%
  filter(tipo_simplificado == "Estudante", !is.na(escola)) %>%
  count(escola, sort = TRUE) %>%
  slice_head(n = 10)

# Gráfico das Escolas
ggplot(top_escolas, aes(x = reorder(str_wrap(escola, 35), n), y = n)) + 
  geom_col(fill = "#4e79a7") +
  geom_text(aes(label = n), hjust = -0.1, size = 3) +
  coord_flip() +
  labs(
    title = "Top 10 Escolas com Maior Uso de Gratuidade",
    subtitle = "Instituições com maior fluxo de alunos no transporte público",
    x = "Instituição de Ensino",
    y = "Quantidade de Viagens"
  ) +
  theme_light()

Interpretação: As escolas listadas acima representam pontos focais de demanda. Linhas que servem essas instituições tendem a sofrer superlotação nos horários de entrada e saída, sugerindo a necessidade de reforço operacional ou ônibus exclusivos nestes trechos.

4.3 Ranking de Operadoras

Quais empresas transportaram a maior quantidade de passageiros gratuitos?

top_operadoras <- dados_processados %>%
  count(operadora, sort = TRUE) %>%
  slice_head(n = 10)

datatable(top_operadoras,
          colnames = c("Operadora", "Total de Gratuidades"),
          caption = "As 10 operadoras com maior volume de gratuidades",
          options = list(pageLength = 5, dom = 't'))

4.4 Comparativo de Horários: Estudantes vs. Idosos

A análise abaixo cruza o horário da transação com o tipo de usuário. Observa-se claramente os picos escolares (entrada e saída) em contraste com o uso mais distribuído dos idosos.

dados_horario_comp <- dados_processados %>%
  filter(tipo_simplificado %in% c("Estudante", "Idoso"), !is.na(hora)) %>%
  group_by(hora, tipo_simplificado) %>%
  summarise(total = n(), .groups = "drop")

ggplot(dados_horario_comp, aes(x = hora, y = total, color = tipo_simplificado)) +
  geom_line(linewidth = 1.2) +
  geom_point() +
  scale_x_continuous(breaks = seq(0, 23, 2)) +
  labs(
    title = "Dinâmica Temporal: Estudantes vs. Idosos",
    subtitle = "Comparação dos picos de utilização ao longo do dia",
    x = "Hora do Dia",
    y = "Volume de Passageiros",
    color = "Categoria"
  ) +
  theme_minimal() +
  theme(legend.position = "top")

Interpretação: 1. Estudantes: Tipicamente apresentam dois picos agudos (início da manhã e meio da tarde), coincidindo com os turnos escolares. Esse comportamento é inelástico (eles não podem escolher outro horário). 2. Idosos: Tendem a ter uma distribuição mais suave ou picos fora do horário de rush extremo. 3. Conclusão Operacional: Se os picos coincidirem com o horário de rush dos trabalhadores, o sistema entra em estresse máximo. Políticas de incentivo para deslocamento fora de ponta podem ser estudadas baseando-se neste gráfico.

5. Conclusões e Considerações Finais

5.1 Resumo dos Achados

A análise dos dados do dia 07/11/2025 demonstrou que a gratuidade no transporte do RJ possui padrões claros e previsíveis. Identificamos que a demanda estudantil é altamente concentrada em horários e locais específicos (polos escolares), enquanto outros grupos apresentam maior dispersão.

5.2 Implicações para o Negócio

Para os gestores públicos e operadores privados, estes dados sugerem: * Alocação Dinâmica: Possibilidade de reforçar linhas específicas nos horários de pico escolar identificados no gráfico 4.4. * Compensação Justa: O ranking de operadoras fornece base para auditoria dos repasses financeiros de subsídios.

5.3 Limitações e Trabalhos Futuros

Esta análise, embora robusta, possui limitações: 1. Recorte Temporal: Analisamos apenas um dia. Uma análise semanal completa evitaria vieses de dias atípicos. 2. Geolocalização: Os dados não possuem coordenadas GPS de origem/destino, impedindo uma análise espacial profunda (mapas de calor).

Para projetos futuros, sugere-se a integração com dados de GPS da frota para mapear não apenas quem usa, mas onde a gratuidade é mais demandada geograficamente.