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.
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.
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?
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).
library(tidyverse)
library(lubridate)
library(janitor)
library(knitr)
library(DT)
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).
TRANSACAO_GRATUIDADE_PUBLICO_2025_11_07.csv# 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")))
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 |
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:
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.
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.
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'))
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.
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.
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.
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.