Resumindo:
- MACD é o coração da análise de tendência.
- Linha de Sinal é o guia comparativo.
- Histograma mostra o “ritmo da dança” entre os dois.
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.5.1 ✔ tibble 3.2.1
## ✔ lubridate 1.9.4 ✔ tidyr 1.3.1
## ✔ purrr 1.0.4
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(TTR)
set.seed(42)
tempo <- seq.POSIXt(from = as.POSIXct("2025-06-29 16:00"), by = "min", length.out = 100)
velocidade <- pmin(pmax(rnorm(100, mean = 40, sd = 7), 10), 60)
atraso <- pmin(pmax(rnorm(100, mean = 5, sd = 2), 1), 15)
intensidade <- round(pmin(pmax((60 - velocidade) / 10, 1), 5))
df <- tibble(
horario = tempo,
velocidade_kmh = velocidade,
atraso_min = atraso,
intensidade = intensidade
)
df
## # A tibble: 100 × 4
## horario velocidade_kmh atraso_min intensidade
## <dttm> <dbl> <dbl> <dbl>
## 1 2025-06-29 16:00:00 49.6 7.40 1
## 2 2025-06-29 16:01:00 36.0 7.09 2
## 3 2025-06-29 16:02:00 42.5 2.99 2
## 4 2025-06-29 16:03:00 44.4 8.70 2
## 5 2025-06-29 16:04:00 42.8 3.67 2
## 6 2025-06-29 16:05:00 39.3 5.21 2
## 7 2025-06-29 16:06:00 50.6 4.16 1
## 8 2025-06-29 16:07:00 39.3 4.76 2
## 9 2025-06-29 16:08:00 54.1 5.38 1
## 10 2025-06-29 16:09:00 39.6 5.24 2
## # ℹ 90 more rows
macd_result <- MACD(df$velocidade_kmh, nFast = 5, nSlow = 15, nSig = 9, maType = EMA)
df <- df %>%
mutate(
MACD = macd_result[, 1],
Sinal = macd_result[, 2],
Histograma = MACD - Sinal
)
df
## # A tibble: 100 × 7
## horario velocidade_kmh atraso_min intensidade MACD Sinal
## <dttm> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2025-06-29 16:00:00 49.6 7.40 1 NA NA
## 2 2025-06-29 16:01:00 36.0 7.09 2 NA NA
## 3 2025-06-29 16:02:00 42.5 2.99 2 NA NA
## 4 2025-06-29 16:03:00 44.4 8.70 2 NA NA
## 5 2025-06-29 16:04:00 42.8 3.67 2 NA NA
## 6 2025-06-29 16:05:00 39.3 5.21 2 NA NA
## 7 2025-06-29 16:06:00 50.6 4.16 1 NA NA
## 8 2025-06-29 16:07:00 39.3 4.76 2 NA NA
## 9 2025-06-29 16:08:00 54.1 5.38 1 NA NA
## 10 2025-06-29 16:09:00 39.6 5.24 2 NA NA
## # ℹ 90 more rows
## # ℹ 1 more variable: Histograma <dbl>
df_long <- df %>%
pivot_longer(cols = c("MACD", "Sinal"), names_to = "linha", values_to = "valor")
ggplot(df_long, aes(x = horario, y = valor, color = linha)) +
geom_line(size = 1) +
geom_col(data = df, aes(x = horario, y = Histograma), fill = "gray70", inherit.aes = FALSE, alpha = 0.5) +
labs(title = "MACD aplicado à Velocidade Média de Trânsito",
x = "Horário", y = "Valor") +
theme_minimal() +
theme(legend.position = "top")
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: Removed 36 rows containing missing values or values outside the scale range
## (`geom_line()`).
## Warning: Removed 22 rows containing missing values or values outside the scale range
## (`geom_col()`).
rm(list = ls())
library(tidyverse)
library(TTR)
tempo <- seq.POSIXt(from = as.POSIXct("2025-06-29 06:00"),
to = as.POSIXct("2025-06-29 21:59"),
by = "min")
n <- length(tempo)
Trânsito mais lento nos horários de pico (7h–9h e 17h–19h)
hora <- hour(tempo)
velocidade_base <- 50 - 15 * ((hora >= 7 & hora <= 9) | (hora >= 17 & hora <= 19))
ruido <- rnorm(n, mean = 0, sd = 5)
velocidade <- pmin(pmax(velocidade_base + ruido, 10), 60)
atraso <- pmin(pmax(15 - velocidade/5 + rnorm(n, 0, 1.5), 1), 20)
intensidade <- round(pmin(pmax((60 - velocidade) / 10, 1), 5))
df <- tibble(
horario = tempo,
velocidade_kmh = velocidade,
atraso_min = atraso,
intensidade = intensidade
)
macd_result <- MACD(df$velocidade_kmh, nFast = 5, nSlow = 15, nSig = 9, maType = EMA)
df <- df %>%
mutate(
MACD = macd_result[, 1],
Sinal = macd_result[, 2],
Histograma = MACD - Sinal
)
df_long <- df %>%
pivot_longer(cols = c("MACD", "Sinal"), names_to = "linha", values_to = "valor")
ggplot(df_long, aes(x = horario, y = valor, color = linha)) +
geom_line(size = 0.9) +
geom_col(data = df, aes(x = horario, y = Histograma),
fill = "gray70", inherit.aes = FALSE, alpha = 0.4) +
labs(title = "MACD aplicado à Velocidade Média (06h–22h)",
x = "Horário", y = "Valor") +
theme_minimal() +
scale_x_datetime(date_breaks = "2 hour", date_labels = "%H:%M") +
theme(legend.position = "top")
## Warning: Removed 36 rows containing missing values or values outside the scale range
## (`geom_line()`).
## Warning: Removed 22 rows containing missing values or values outside the scale range
## (`geom_col()`).
rm(list = ls())
library(tidyverse)
library(TTR)
set.seed(42)
ruas <- c("Av Brasil", "Rua das Flores", "Av Central")
tempo <- seq.POSIXt(from = as.POSIXct("2025-06-29 06:00"),
to = as.POSIXct("2025-06-29 21:59"),
by = "min")
n <- length(tempo)
# Gerar DataFrame para todas as ruas
dados <- map_dfr(ruas, function(rua) {
velocidade_base <- 50 - 15 * ((hour(tempo) >= 7 & hour(tempo) <= 9) | (hour(tempo) >= 17 & hour(tempo) <= 19))
ruido <- rnorm(n, 0, 6)
velocidade <- pmin(pmax(velocidade_base + ruido + runif(1, -5, 5), 10), 60)
tibble(
rua = rua,
horario = tempo,
velocidade_kmh = velocidade
)
})
dados_macd <- dados %>%
group_by(rua) %>%
group_modify(~{
m <- MACD(.x$velocidade_kmh, nFast = 5, nSlow = 15, nSig = 9)
mutate(.x,
MACD = m[,1],
Sinal = m[,2],
Histograma = m[,1] - m[,2])
})
dados_long <- dados_macd %>%
pivot_longer(cols = c("MACD", "Sinal"), names_to = "linha", values_to = "valor")
ggplot(dados_long, aes(x = horario, y = valor, color = linha)) +
geom_line() +
geom_col(data = dados_macd, aes(x = horario, y = Histograma),
inherit.aes = FALSE, fill = "gray75", alpha = 0.5) +
facet_wrap(~ rua, scales = "free_y") +
labs(title = "MACD da Velocidade por Rua",
x = "Horário", y = "Valor") +
theme_minimal() +
theme(legend.position = "top")
## Warning: Removed 36 rows containing missing values or values outside the scale range
## (`geom_line()`).
## Warning: Removed 66 rows containing missing values or values outside the scale range
## (`geom_col()`).
🔻 MACD < -4
- Isso indica que a velocidade média da via está muito abaixo da média
recente, sugerindo trânsito travado ou piora acentuada.
- Um valor abaixo de -4 não é absoluto — depende da escala dos seus
dados — mas, na prática, você está dizendo:
- “Se a fluidez piorou o suficiente para o MACD cair mais de 4 unidades
abaixo da linha de base, acione um alerta.”
🔺 Histograma > 3
- Significa que a diferença entre o MACD e a linha de sinal está
bastante positiva. Traduzindo:
- O tráfego está melhorando rapidamente, ou
- O MACD está “disparando para cima” mais do que a média recente
esperaria.
💡 Resumo prático
MACD < -4 → alerta para congestionamento severo.
Histograma > 3 → alerta para recuperação rápida ou reversão de
tendência.
df_alertas <- dados_macd %>%
filter(MACD < -4 | Histograma > 3)
df_alertas
## # A tibble: 882 × 6
## # Groups: rua [3]
## rua horario velocidade_kmh MACD Sinal Histograma
## <chr> <dttm> <dbl> <dbl> <dbl> <dbl>
## 1 Av Brasil 2025-06-29 06:14:00 52.5 -4.05 NA NA
## 2 Av Brasil 2025-06-29 06:17:00 37.3 -8.79 NA NA
## 3 Av Brasil 2025-06-29 06:18:00 38.6 -11.8 NA NA
## 4 Av Brasil 2025-06-29 06:19:00 60 -4.04 NA NA
## 5 Av Brasil 2025-06-29 06:21:00 42.6 -5.84 NA NA
## 6 Av Brasil 2025-06-29 06:23:00 60 1.48 -3.73 5.21
## 7 Av Brasil 2025-06-29 06:24:00 60 4.07 -2.17 6.25
## 8 Av Brasil 2025-06-29 06:25:00 50.7 1.79 -1.38 3.17
## 9 Av Brasil 2025-06-29 06:32:00 59.5 4.30 0.740 3.56
## 10 Av Brasil 2025-06-29 06:38:00 38.8 -7.83 -2.44 -5.39
## # ℹ 872 more rows
rm(list = ls())
library(tidyverse)
library(lubridate)
library(TTR)
set.seed(42)
ruas <- c("Av Brasil", "Rua das Flores", "Av Central")
dias <- seq.Date(from = as.Date("2025-01-01"), to = as.Date("2025-06-30"), by = "day")
minutos_dia <- seq.POSIXt(from = as.POSIXct("06:00", format="%H:%M"),
to = as.POSIXct("21:59", format="%H:%M"), by = "min")
n <- length(minutos_dia)
dados <- expand.grid(
data = dias,
rua = ruas,
minuto = minutos_dia
) %>%
mutate(
horario = as.POSIXct(paste(data, format(minuto, "%H:%M:%S"))),
hora = hour(horario),
# Base = velocidade teórica para o horário
base = 50 - 15 * ((hora >= 7 & hora <= 9) | (hora >= 17 & hora <= 19)),
# Estamos usando uma distribuição normal com média 0 e desvio padrão 6 para gerar pequenas variações;
ruido = rnorm(n() , 0, 5),
velocidade_kmh = pmin(pmax(base + ruido + runif(n(), -5, 5), 10), 60)
)
head(dados)
## data rua minuto horario hora base
## 1 2025-01-01 Av Brasil 2025-07-07 06:00:00 2025-01-01 06:00:00 6 50
## 2 2025-01-02 Av Brasil 2025-07-07 06:00:00 2025-01-02 06:00:00 6 50
## 3 2025-01-03 Av Brasil 2025-07-07 06:00:00 2025-01-03 06:00:00 6 50
## 4 2025-01-04 Av Brasil 2025-07-07 06:00:00 2025-01-04 06:00:00 6 50
## 5 2025-01-05 Av Brasil 2025-07-07 06:00:00 2025-01-05 06:00:00 6 50
## 6 2025-01-06 Av Brasil 2025-07-07 06:00:00 2025-01-06 06:00:00 6 50
## ruido velocidade_kmh
## 1 6.8547922 53.89820
## 2 -2.8234909 43.84592
## 3 1.8156421 52.81086
## 4 3.1643130 54.72173
## 5 2.0213416 52.08769
## 6 -0.5306226 51.10586
library(tidyverse)
library(TTR)
dados_macd <- dados %>%
group_by(rua, data) %>%
group_modify(~{
resultado <- MACD(.x$velocidade_kmh, nFast = 5, nSlow = 15, nSig = 9)
.x %>%
mutate(
MACD = resultado[, 1],
Sinal = resultado[, 2],
Histograma = MACD - Sinal
)
}) %>%
ungroup()
head(dados_macd)
## # A tibble: 6 × 11
## rua data minuto horario hora base ruido
## <fct> <date> <dttm> <dttm> <int> <dbl> <dbl>
## 1 Av Bras… 2025-01-01 2025-07-07 06:00:00 2025-01-01 06:00:00 6 50 6.85
## 2 Av Bras… 2025-01-01 2025-07-07 06:01:00 2025-01-01 06:01:00 6 50 8.04
## 3 Av Bras… 2025-01-01 2025-07-07 06:02:00 2025-01-01 06:02:00 6 50 -1.99
## 4 Av Bras… 2025-01-01 2025-07-07 06:03:00 2025-01-01 06:03:00 6 50 2.14
## 5 Av Bras… 2025-01-01 2025-07-07 06:04:00 2025-01-01 06:04:00 6 50 -2.92
## 6 Av Bras… 2025-01-01 2025-07-07 06:05:00 2025-01-01 06:05:00 6 50 -0.882
## # ℹ 4 more variables: velocidade_kmh <dbl>, MACD <dbl>, Sinal <dbl>,
## # Histograma <dbl>
#Criar um índice de criticidade diária
mean(dados_macd$Histograma, na.rm = TRUE)
## [1] 0.000444561
quantile(dados_macd$Histograma, na.rm = TRUE, probs = c(.5,.75,.80,.85,.90,.95,1))
## 50% 75% 80% 85% 90% 95%
## 0.007388733 1.855033188 2.318450744 2.864334727 3.569072768 4.715503297
## 100%
## 17.210814638
resumo_dias <- dados_macd %>%
group_by(rua, data) %>%
summarise(
tempo_congestionado = sum(MACD < Sinal, na.rm = TRUE),
#histograma_médio = mean(abs(Histograma), na.rm = TRUE),
histograma_médio = quantile(dados_macd$Histograma, na.rm = TRUE, probs = c(.95)),# quantil 95
pico_negativo = sum(Histograma < -3, na.rm = TRUE),
pico_positivo = sum(Histograma > 3, na.rm = TRUE)
) %>%
arrange(desc(tempo_congestionado))
## `summarise()` has grouped output by 'rua'. You can override using the `.groups`
## argument.
head(resumo_dias)
## # A tibble: 6 × 6
## # Groups: rua [2]
## rua data tempo_congestionado histograma_médio pico_negativo
## <fct> <date> <int> <dbl> <int>
## 1 Av Brasil 2025-03-10 494 4.72 129
## 2 Av Central 2025-06-20 493 4.72 131
## 3 Av Brasil 2025-01-23 492 4.72 124
## 4 Av Brasil 2025-02-17 492 4.72 133
## 5 Av Brasil 2025-05-11 492 4.72 134
## 6 Av Central 2025-04-20 492 4.72 127
## # ℹ 1 more variable: pico_positivo <int>
ggplot(resumo_dias, aes(x = data, y = rua, fill = tempo_congestionado)) +
geom_tile(color = "white") +
scale_fill_viridis_c(option = "inferno") +
labs(title = "Mapa de calor: dias com maior tempo de congestionamento",
x = "Data", y = "Rua", fill = "Minutos Congestionado") +
theme_minimal()
#### MACD ao longo do tempo (por rua)
ggplot(dados_macd, aes(x = horario, y = MACD, color = rua)) +
geom_line(size = 0.6, alpha = 0.8) +
labs(title = "MACD da Velocidade ao Longo do Mês",
x = "Horário", y = "MACD") +
theme_minimal() +
facet_wrap(~ rua, scales = "free_y", ncol = 1) +
theme(legend.position = "none")
## Warning: Removed 42 rows containing missing values or values outside the scale range
## (`geom_line()`).
resumo_dias <- dados_macd %>%
mutate(data = as.Date(horario)) %>%
group_by(rua, data) %>%
summarise(
minutos_criticos = sum(MACD < -10 | Histograma > 5, na.rm = TRUE),
.groups = "drop"
)
ggplot(resumo_dias, aes(x = data, y = rua, fill = minutos_criticos)) +
geom_tile(color = "white") +
scale_fill_viridis_c(option = "plasma", name = "Minutos Críticos") +
labs(title = "Dias Críticos por Rua segundo MACD",
x = "Data", y = "Rua") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
## Gráfico com MACD, Linha de Sinal e Histograma
# Transformar dados para gráfico (exibindo MACD e Sinal)
dados_long <- dados_macd %>%
pivot_longer(cols = c("MACD", "Sinal"), names_to = "linha", values_to = "valor")
# Plotar MACD e Sinal com Histograma de fundo
ggplot(dados_long, aes(x = horario, y = valor, color = linha)) +
geom_line(size = 0.7) +
geom_col(data = dados_macd, aes(x = horario, y = Histograma),
inherit.aes = FALSE, fill = "gray75", alpha = 0.4) +
facet_wrap(~ rua, scales = "free_y", ncol = 1) +
labs(title = "MACD, Linha de Sinal e Histograma por Rua",
x = "Horário", y = "Valor do Indicador") +
theme_minimal() +
theme(legend.position = "top")
## Warning: Removed 36 rows containing missing values or values outside the scale range
## (`geom_line()`).
## Warning: Removed 11946 rows containing missing values or values outside the scale range
## (`geom_col()`).
library(ggplot2)
library(dplyr)
library(tidyr)
# Filtra uma rua específica
dados_filtrados <- dados_macd %>%
dplyr::filter(rua == "Av Brasil") %>%
dplyr::select(horario, MACD, Sinal, Histograma)
# Reorganiza os dados para MACD e Sinal em linhas separadas
dados_long <- dados_filtrados %>%
pivot_longer(cols = c("MACD", "Sinal"), names_to = "linha", values_to = "valor")
# Plotagem
ggplot(dados_long, aes(x = horario, y = valor, color = linha)) +
geom_line(size = 0.7) +
geom_col(data = dados_filtrados, aes(x = horario, y = Histograma),
fill = "gray70", alpha = 0.5, inherit.aes = FALSE) +
labs(title = "MACD, Sinal e Histograma – Av Brasil",
x = "Horário", y = "Indicador") +
theme_minimal() +
theme(legend.position = "top")
## Warning: Removed 36 rows containing missing values or values outside the scale range
## (`geom_line()`).
## Warning: Removed 3982 rows containing missing values or values outside the scale range
## (`geom_col()`).
dias_rotulados <- resumo_dias %>%
mutate(critico = minutos_criticos >= 60) # ajuste esse threshold se desejar
Para prever a semana seguinte, criamos estatísticas da semana anterior (última segunda a sexta) — média de minutos críticos, desvio, nº de dias críticos, etc. Isolar os últimos 5 dias úteis da série
ultima_semana <- dias_rotulados %>%
filter(weekdays(data) %in% c("segunda-feira", "terça-feira", "quarta-feira", "quinta-feira", "sexta-feira")) %>%
group_by(rua) %>%
filter(data == max(data[data < max(data)])) %>% # último dia útil da semana anterior
summarise(
media_crit = mean(minutos_criticos, na.rm = TRUE),
dias_crit = sum(critico),
pico_max = max(minutos_criticos)
)
Como seus dados são simulados, você pode usar uma regra de decisão
simples:
Se a rua teve mais de 2 dias críticos na última semana ou média > 60
minutos → alta chance de semana seguinte também ter dias críticos
prognostico <- ultima_semana %>%
mutate(provavel_semana_critica = media_crit > 60 | dias_crit >= 3)
prognostico
## # A tibble: 3 × 5
## rua media_crit dias_crit pico_max provavel_semana_critica
## <fct> <dbl> <int> <int> <lgl>
## 1 Av Brasil 59 0 59 FALSE
## 2 Rua das Flores 61 1 61 TRUE
## 3 Av Central 56 0 56 FALSE
quantis <- resumo_dias %>%
summarise(
Q1 = quantile(minutos_criticos, 0.25),
Q3 = quantile(minutos_criticos, 0.75)
)
threshold <- quantis$Q3
resumo_dias <- resumo_dias %>%
mutate(critico = minutos_criticos >= threshold)
resumo_dias
## # A tibble: 546 × 4
## rua data minutos_criticos critico
## <fct> <date> <int> <lgl>
## 1 Av Brasil 2025-01-01 64 FALSE
## 2 Av Brasil 2025-01-02 77 TRUE
## 3 Av Brasil 2025-01-03 56 FALSE
## 4 Av Brasil 2025-01-04 61 FALSE
## 5 Av Brasil 2025-01-05 56 FALSE
## 6 Av Brasil 2025-01-06 65 TRUE
## 7 Av Brasil 2025-01-07 83 TRUE
## 8 Av Brasil 2025-01-08 69 TRUE
## 9 Av Brasil 2025-01-09 67 TRUE
## 10 Av Brasil 2025-01-10 63 FALSE
## # ℹ 536 more rows
Vamos usar os dados históricos de cada rua para calcular estatísticas agregadas por dia, como: Média móvel do número de minutos críticos (últimos 3 a 5 dias) Tendência da média do MACD (sinal de agravamento ou melhora) Quantidade de picos negativos e positivos recentes Exemplo para 5 dias anteriores por rua:
resumo_dias <- dados_macd %>%
mutate(data = as.Date(horario)) %>%
group_by(rua, data) %>%
summarise(
minutos_criticos = sum(MACD < -10 | Histograma > (5), na.rm = TRUE),
pico_negativo = sum(Histograma < -5, na.rm = TRUE),
pico_positivo = sum(Histograma > 5, na.rm = TRUE),
.groups = "drop"
)
###✅ Passo 2: aplicar as médias móveis com slide_dbl()
library(slider)
resumo_vars <- resumo_dias %>%
arrange(rua, data) %>%
group_by(rua) %>%
mutate(
mm_criticos = slide_dbl(minutos_criticos, mean, .before = 5, .complete = TRUE),
mm_pico_neg = slide_dbl(pico_negativo, mean, .before = 5, .complete = TRUE),
mm_pico_pos = slide_dbl(pico_positivo, mean, .before = 5, .complete = TRUE),
critico = minutos_criticos >= quantile(minutos_criticos, 0.75, na.rm = TRUE)
) %>%
ungroup() %>%
drop_na()
library(xgboost)
##
## Attaching package: 'xgboost'
## The following object is masked from 'package:dplyr':
##
## slice
set.seed(123)
train_idx <- sample(nrow(resumo_vars), 0.8 * nrow(resumo_vars))
train <- resumo_vars[train_idx, ]
test <- resumo_vars[-train_idx, ]
x_train <- as.matrix(select(train, mm_criticos, mm_pico_neg, mm_pico_pos))
y_train <- as.numeric(train$critico)
x_test <- as.matrix(select(test, mm_criticos, mm_pico_neg, mm_pico_pos))
y_test <- as.numeric(test$critico)
modelo <- xgboost(
data = x_train,
label = y_train,
nrounds = 50,
objective = "binary:logistic",
eval_metric = "logloss",
verbose = 0
)
pred_prob <- predict(modelo, x_test)
pred_class <- as.integer(pred_prob > 0.5)
confusion <- table(Predito = pred_class, Real = y_test)
print(confusion)
## Real
## Predito 0 1
## 0 64 18
## 1 16 9
Para visualizar o que o modelo mais usou para prever dias
críticos:
Esse gráfico mostra o peso de cada variável (ex: mm_criticos,
mm_pico_neg, etc.) — ou seja, quais padrões o modelo mais considera na
hora de decidir.
library(xgboost)
xgb.plot.importance(xgb.importance(model = modelo))
A AUC (Área Sob a Curva) de 0.6364 significa que o seu modelo tem capacidade razoável de distinguir entre dias críticos e não críticos — melhor do que um chute aleatório (que seria 0.5), mas ainda com espaço para melhorias. 🧭 Como interpretar: 0.5 a 0.6: quase aleatório 0.6 a 0.7: separação modesta — sua faixa atual 0.7 a 0.8: bom desempenho Acima de 0.8: excelente discriminador
library(pROC)
## Type 'citation("pROC")' for a citation.
##
## Attaching package: 'pROC'
## The following objects are masked from 'package:stats':
##
## cov, smooth, var
roc_obj <- roc(response = y_test, predictor = pred_prob)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
plot(roc_obj, col = "steelblue", main = "Curva ROC – Previsão de Dias Críticos")
auc(roc_obj) # Área sob a curva — quanto mais perto de 1, melhor
## Area under the curve: 0.6653
✅ 1. Isolar os dados da última semana observada
Últimos 5 dias da série
ultima_data <- max(resumo_vars$data)
semana_final <- resumo_vars %>%
filter(data > (ultima_data - 7)) %>%
group_by(rua) %>%
filter(row_number(desc(data)) <= 5) %>%
arrange(rua, data)
🛠️ 2. Criar observações preditivas por rua
A ideia aqui é gerar uma linha por rua com a média móvel mais recente
dos últimos 5 dias:
dados_prev <- semana_final %>%
group_by(rua) %>%
summarise(
mm_criticos = mean(minutos_criticos, na.rm = TRUE),
mm_pico_neg = mean(pico_negativo, na.rm = TRUE),
mm_pico_pos = mean(pico_positivo, na.rm = TRUE)
)
🤖 3. Prever com o modelo xgboost
Matriz com as variáveis preditoras
x_prev <- as.matrix(select(dados_prev, mm_criticos, mm_pico_neg, mm_pico_pos))
Probabilidades previstas
probabilidades <- predict(modelo, x_prev)
Classificação binária (ajuste o threshold se quiser mais ou menos sensibilidade)
classe_prevista <- ifelse(probabilidades > 0.5, "Crítica", "Normal")
dados_prev$prognostico_semana <- classe_prevista
dados_prev
## # A tibble: 3 × 5
## rua mm_criticos mm_pico_neg mm_pico_pos prognostico_semana
## <fct> <dbl> <dbl> <dbl> <chr>
## 1 Av Brasil 50.6 33 32.6 Normal
## 2 Rua das Flores 45.4 36.2 32.2 Normal
## 3 Av Central 48 35 33.6 Normal
ggplot(dados_prev, aes(x = reorder(rua, -probabilidades), y = probabilidades, fill = prognostico_semana)) +
geom_col(width = 0.6) +
geom_text(aes(label = scales::percent(probabilidades, accuracy = 1)),
vjust = -0.5, size = 4) +
scale_fill_manual(values = c("Crítica" = "#E63946", "Normal" = "#2A9D8F")) +
labs(title = "Prognóstico da Semana Seguinte por Rua",
x = "Rua", y = "Probabilidade de Semana Crítica") +
theme_minimal() +
ylim(0, 1) +
theme(legend.position = "top")
### retornando para semanas anteriores
gerar_prognostico <- function(df, modelo, semana_referencia = 0, plotar = TRUE) {
library(ggplot2)
ultima_data <- max(df$data)
data_fim <- ultima_data - weeks(semana_referencia)
data_ini <- data_fim - 4 # segunda a sexta
# Filtrar semana desejada
semana_ref <- df %>%
filter(data >= data_ini & data <= data_fim)
# Agregar variáveis por rua
dados_prev <- semana_ref %>%
group_by(rua) %>%
summarise(
mm_criticos = mean(minutos_criticos, na.rm = TRUE),
mm_pico_neg = mean(pico_negativo, na.rm = TRUE),
mm_pico_pos = mean(pico_positivo, na.rm = TRUE),
.groups = "drop"
)
# Prever com o modelo
x_pred <- as.matrix(select(dados_prev, mm_criticos, mm_pico_neg, mm_pico_pos))
prob <- predict(modelo, x_pred)
classe <- ifelse(prob > 0.5, "Crítica", "Normal")
# Adiciona colunas à tabela final
dados_prev <- dados_prev %>%
mutate(
semana_analizada = paste(format(data_ini, "%d/%m"), "a", format(data_fim, "%d/%m")),
prognostico = classe,
probabilidade = prob
)
# Gera gráfico se solicitado
if (plotar) {
p <- ggplot(dados_prev, aes(x = reorder(rua, -probabilidade), y = probabilidade, fill = prognostico)) +
geom_col(width = 0.6) +
geom_text(aes(label = scales::percent(probabilidade, accuracy = 1)),
vjust = -0.5, size = 4) +
scale_fill_manual(values = c("Crítica" = "#E63946", "Normal" = "#2A9D8F")) +
labs(title = paste("Prognóstico da Semana:", format(data_ini, "%d/%m"), "a", format(data_fim, "%d/%m")),
x = "Rua", y = "Probabilidade de Semana Crítica") +
ylim(0, 1) +
theme_minimal() +
theme(legend.position = "top")
print(p) # exibir gráfico
}
return(dados_prev) # retornar tabela também
}
gerar_prognostico(resumo_dias, modelo, semana_referencia = 0)
## # A tibble: 3 × 7
## rua mm_criticos mm_pico_neg mm_pico_pos semana_analizada prognostico
## <fct> <dbl> <dbl> <dbl> <chr> <chr>
## 1 Av Brasil 50.6 33 32.6 27/06 a 01/07 Normal
## 2 Rua das Flor… 45.4 36.2 32.2 27/06 a 01/07 Normal
## 3 Av Central 48 35 33.6 27/06 a 01/07 Normal
## # ℹ 1 more variable: probabilidade <dbl>
gerar_prognostico(resumo_dias, modelo, semana_referencia = 1)
## # A tibble: 3 × 7
## rua mm_criticos mm_pico_neg mm_pico_pos semana_analizada prognostico
## <fct> <dbl> <dbl> <dbl> <chr> <chr>
## 1 Av Brasil 60.2 38.6 40 20/06 a 24/06 Normal
## 2 Rua das Flor… 60.4 40.6 40.8 20/06 a 24/06 Crítica
## 3 Av Central 54.6 40 36.8 20/06 a 24/06 Normal
## # ℹ 1 more variable: probabilidade <dbl>
gerar_prognostico(resumo_dias, modelo, semana_referencia = 2)
## # A tibble: 3 × 7
## rua mm_criticos mm_pico_neg mm_pico_pos semana_analizada prognostico
## <fct> <dbl> <dbl> <dbl> <chr> <chr>
## 1 Av Brasil 68.2 43.8 47.2 13/06 a 17/06 Crítica
## 2 Rua das Flor… 58.8 38.4 40.6 13/06 a 17/06 Normal
## 3 Av Central 58.8 41.4 40.4 13/06 a 17/06 Crítica
## # ℹ 1 more variable: probabilidade <dbl>
gerar_prognostico(resumo_dias, modelo, semana_referencia = 4)
## # A tibble: 3 × 7
## rua mm_criticos mm_pico_neg mm_pico_pos semana_analizada prognostico
## <fct> <dbl> <dbl> <dbl> <chr> <chr>
## 1 Av Brasil 61.2 37.8 39 30/05 a 03/06 Normal
## 2 Rua das Flor… 65.6 43 42.8 30/05 a 03/06 Normal
## 3 Av Central 57.4 41.4 37.6 30/05 a 03/06 Normal
## # ℹ 1 more variable: probabilidade <dbl>
gerar_prognostico(resumo_dias, modelo, semana_referencia = 5)
## # A tibble: 3 × 7
## rua mm_criticos mm_pico_neg mm_pico_pos semana_analizada prognostico
## <fct> <dbl> <dbl> <dbl> <chr> <chr>
## 1 Av Brasil 54 38.8 36.6 23/05 a 27/05 Normal
## 2 Rua das Flor… 64.8 43.4 42.8 23/05 a 27/05 Normal
## 3 Av Central 61.2 41.8 40.2 23/05 a 27/05 Normal
## # ℹ 1 more variable: probabilidade <dbl>
gerar_prognostico(resumo_dias, modelo, semana_referencia = 6)
## # A tibble: 3 × 7
## rua mm_criticos mm_pico_neg mm_pico_pos semana_analizada prognostico
## <fct> <dbl> <dbl> <dbl> <chr> <chr>
## 1 Av Brasil 63.2 45 41.6 16/05 a 20/05 Normal
## 2 Rua das Flor… 57.8 44 40.8 16/05 a 20/05 Normal
## 3 Av Central 56 40.8 36.2 16/05 a 20/05 Normal
## # ℹ 1 more variable: probabilidade <dbl>
gerar_prognostico(resumo_dias, modelo, semana_referencia = 7)
## # A tibble: 3 × 7
## rua mm_criticos mm_pico_neg mm_pico_pos semana_analizada prognostico
## <fct> <dbl> <dbl> <dbl> <chr> <chr>
## 1 Av Brasil 64.2 45.2 43.6 09/05 a 13/05 Normal
## 2 Rua das Flor… 56.4 41.6 36.4 09/05 a 13/05 Normal
## 3 Av Central 66.4 43.4 45.8 09/05 a 13/05 Crítica
## # ℹ 1 more variable: probabilidade <dbl>
gerar_prognostico(resumo_dias, modelo, semana_referencia = 8)
## # A tibble: 3 × 7
## rua mm_criticos mm_pico_neg mm_pico_pos semana_analizada prognostico
## <fct> <dbl> <dbl> <dbl> <chr> <chr>
## 1 Av Brasil 59.6 38 39.2 02/05 a 06/05 Normal
## 2 Rua das Flor… 58.6 43.8 37.6 02/05 a 06/05 Normal
## 3 Av Central 53.6 39.8 37 02/05 a 06/05 Normal
## # ℹ 1 more variable: probabilidade <dbl>
gerar_prognostico(resumo_dias, modelo, semana_referencia = 9)
## # A tibble: 3 × 7
## rua mm_criticos mm_pico_neg mm_pico_pos semana_analizada prognostico
## <fct> <dbl> <dbl> <dbl> <chr> <chr>
## 1 Av Brasil 63.2 42.8 44.2 25/04 a 29/04 Crítica
## 2 Rua das Flor… 61.2 44.6 40.2 25/04 a 29/04 Normal
## 3 Av Central 61.2 40.8 39.4 25/04 a 29/04 Normal
## # ℹ 1 more variable: probabilidade <dbl>