df_raw <- read_parquet(PATH_OCORR_CLIMA)
df_raw <- df_raw %>% left_join(segments, by="segmento")
df <- df_raw |>
filter(coord_valida) |>
mutate(
ano = year(data_inicio),
mes = month(data_inicio),
mes_lab = month(data_inicio, label = TRUE, abbr = TRUE),
evento_precip_ext = tp_above_p95 == 1,
evento_vento_forte = ws10_above_60kmh == 1,
evento_calor = dia_calor_35 == 1,
evento_gelo = ciclo_gelo_degelo == 1,
qualquer_extremo = evento_precip_ext | evento_vento_forte |
evento_calor | evento_gelo
)
tipos_analisar <- df |>
count(tipo_harmonizado, sort = TRUE) |>
filter(n >= MIN_OCORR_TIPO) |>
pull(tipo_harmonizado)
df_tipos <- df |> filter(tipo_harmonizado %in% tipos_analisar)
features_continuas <- c(
"tp_d", "tp_h_max", "tp_acc_3h", "tp_acc_6h", "tp_acc_12h",
"tp_acc_24h", "tp_acc_48h", "tp_acc_72h",
"t2m_min", "t2m_max", "t2m_mean", "amplitude_termica",
"ws10_max", "ws10_mean",
"rh_min", "rh_max", "rh_mean",
"indice_calor", "n_dias_seco_prev"
)
features_binarias <- c(
"tp_above_p95", "tp_above_p99", "tp_above_20mm", "tp_h_above_10",
"dia_calor_35", "dia_calor_40", "dia_frio", "ciclo_gelo_degelo",
"onda_calor", "onda_frio", "ws10_above_60kmh", "ws10_above_80kmh",
"ws10_above_100kmh", "rh_alta", "chuva_vento_forte", "precip_pos_seca"
)liga si
tibble(
Metrica = c(
"Total de ocorrências",
"Com coordenadas válidas",
"Com features climáticas",
"Período coberto",
"Linhas distintas",
"Segmentos distintos",
"Tipos de ocorrência distintos",
paste0("Tipos com >= ", MIN_OCORR_TIPO, " ocorrências (analisáveis)")
),
Valor = c(
format(nrow(df_raw), big.mark = " "),
format(sum(df_raw$coord_valida, na.rm = TRUE), big.mark = " "),
format(sum(!is.na(df_raw$tp_d)), big.mark = " "),
paste(min(df$ano, na.rm = TRUE), "-", max(df$ano, na.rm = TRUE)),
format(n_distinct(df$linha, na.rm = TRUE), big.mark = " "),
format(n_distinct(df$segmento, na.rm = TRUE), big.mark = " "),
format(n_distinct(df$tipo_harmonizado, na.rm = TRUE), big.mark = " "),
as.character(length(tipos_analisar))
)
) |>
kable(align = c("l", "r"), booktabs = TRUE,
col.names = c("Métrica", "Valor")) |>
kable_styling(
bootstrap_options = c("striped", "hover"),
latex_options = c("striped", "hold_position"),
full_width = FALSE
)| Métrica | Valor |
|---|---|
| Total de ocorrências | 206 616 |
| Com coordenadas válidas | 204 593 |
| Com features climáticas | 204 593 |
| Período coberto | 2010 - 2025 |
| Linhas distintas | 61 |
| Segmentos distintos | 71 |
| Tipos de ocorrência distintos | 64 |
| Tipos com >= 200 ocorrências (analisáveis) | 37 |
df |>
count(ano, grupo_harmonizado) |>
ggplot(aes(ano, n, fill = grupo_harmonizado)) +
geom_col() +
scale_fill_manual(
values = colorRampPalette(cores_principais)(n_distinct(df$grupo_harmonizado))
) +
scale_x_continuous(breaks = seq(min(df$ano, na.rm = TRUE),
max(df$ano, na.rm = TRUE), 2)) +
scale_y_continuous(labels = comma) +
labs(title = "Ocorrências por ano e grupo",
x = NULL, y = "N.º de ocorrências", fill = "Grupo") +
guides(fill = guide_legend(nrow = 3))df |>
count(tipo_harmonizado, grupo_harmonizado) |>
mutate(tipo_harmonizado = fct_reorder(tipo_harmonizado, n)) |>
ggplot(aes(n, tipo_harmonizado, fill = grupo_harmonizado)) +
geom_col() +
geom_vline(xintercept = MIN_OCORR_TIPO, linetype = "dashed",
colour = "red", linewidth = 0.7) +
annotate("text", x = MIN_OCORR_TIPO + 20, y = 3,
label = paste0("Limiar análise (", MIN_OCORR_TIPO, ")"),
colour = "red", size = 3, hjust = 0) +
scale_fill_manual(
values = colorRampPalette(cores_principais)(n_distinct(df$grupo_harmonizado))
) +
scale_x_continuous(labels = comma) +
labs(title = "Volume de ocorrências por tipo",
subtitle = "Linha vermelha: limiar mínimo para análise individual",
x = "N.º de ocorrências", y = NULL, fill = "Grupo") +
guides(fill = guide_legend(nrow = 3))df_tipos |>
count(tipo_harmonizado, mes) |>
group_by(tipo_harmonizado) |>
mutate(pct = n / sum(n)) |>
ungroup() |>
mutate(tipo_harmonizado = fct_reorder(
tipo_harmonizado,
ifelse(mes %in% c(11, 12, 1, 2, 3), pct, 0),
.fun = sum
)) |>
ggplot(aes(mes, tipo_harmonizado, fill = pct)) +
geom_tile(colour = "white", linewidth = 0.3) +
scale_x_continuous(
breaks = 1:12,
labels = c("Jan","Fev","Mar","Abr","Mai","Jun",
"Jul","Ago","Set","Out","Nov","Dez")
) +
scale_fill_gradient(low = "#EBF5FB", high = "#1F4E79",
labels = percent_format(accuracy = 1)) +
labs(title = "Distribuição mensal por tipo de ocorrência",
subtitle = "Proporção do total anual",
x = NULL, y = NULL, fill = "% do total") +
theme(axis.text.x = element_text(angle = 30, hjust = 1),
axis.text.y = element_text(size = 8),
legend.position = "right")# df |>
# count(segmento, linha) |>
# slice_max(n, n = 20) |>
# mutate(segmento = fct_reorder(as.character(segmento), n)) %>%
# mutate(n)
top_seg <- df |>
group_by(segmento, linha) |>
summarise(
n = n(),
extensao = first(extensao),
.groups = "drop"
) |>
mutate(n_por_ext = n / extensao) |>
slice_max(n_por_ext, n = 20) |>
mutate(segmento = fct_reorder(as.character(segmento), n_por_ext))
top_seg |>
ggplot(aes(n_por_ext, segmento, fill = as.character(linha))) +
geom_col() +
scale_x_continuous(labels = comma) +
scale_fill_manual(
values = colorRampPalette(cores_principais)(n_distinct(top_seg$linha))
) +
labs(title = "Top 20 segmentos com mais ocorrências relativas (ocorrências / extensão)",
x = "N.º de ocorrências", y = "Segmento", fill = "Linha") +
theme(legend.position = "right",
axis.text.y = element_text(size = 8))# Fronteiras de Portugal (requer pacote rnaturalearth)
# install.packages(c("rnaturalearth", "rnaturalearthdata", "sf"))
portugal <- ne_countries(scale = "medium", country = "Portugal",
returnclass = "sf")
# Contagem de ocorrências por célula ERA5
ocorr_por_celula <- df |>
filter(!is.na(era5_lat), !is.na(era5_lon)) |>
count(era5_lat, era5_lon, name = "n_ocorrencias")
# Mapa base: ocorrências totais por célula ERA5
p_mapa_total <- ggplot() +
geom_sf(data = portugal, fill = "#F5F5F5", colour = "#AAAAAA",
linewidth = 0.5) +
geom_point(
data = ocorr_por_celula,
aes(x = era5_lon, y = era5_lat,
size = n_ocorrencias,
colour = n_ocorrencias),
alpha = 0.85
) +
scale_colour_gradient(low = "#D6E4F0", high = "#1F4E79",
name = "N.º ocorrências",
labels = comma) +
scale_size_continuous(range = c(1, 12),
name = "N.º ocorrências",
labels = comma) +
guides(colour = guide_legend(), size = guide_legend()) +
coord_sf(xlim = c(-9.8, -6.0), ylim = c(36.8, 42.2)) +
labs(
title = "Concentração de ocorrências por célula ERA5-Land",
subtitle = "Cada ponto = 1 célula da grelha (~9 km) | tamanho e cor = n.º de ocorrências",
x = "Longitude", y = "Latitude"
) +
theme(
panel.background = element_rect(fill = "#EBF5FB", colour = NA),
legend.position = "right"
)
print(p_mapa_total)# Mapa por grupo de ocorrência (facet)
ocorr_por_celula_grupo <- df |>
filter(!is.na(era5_lat), !is.na(era5_lon)) |>
count(era5_lat, era5_lon, grupo_harmonizado, name = "n_ocorrencias")
ggplot() +
geom_sf(data = portugal, fill = "#F5F5F5", colour = "#AAAAAA",
linewidth = 0.4) +
geom_point(
data = ocorr_por_celula_grupo,
aes(x = era5_lon, y = era5_lat,
size = n_ocorrencias,
colour = n_ocorrencias),
alpha = 0.8
) +
scale_colour_gradient(low = "#D6E4F0", high = "#C00000",
name = "N.º ocorrências",
labels = comma) +
scale_size_continuous(range = c(0.5, 8),
name = "N.º ocorrências",
labels = comma) +
guides(colour = guide_legend(), size = guide_legend()) +
coord_sf(xlim = c(-9.8, -6.0), ylim = c(36.8, 42.2)) +
facet_wrap(~ grupo_harmonizado, ncol = 3) +
labs(
title = "Distribuição geográfica por grupo de ocorrência",
subtitle = "Células ERA5-Land com pelo menos uma ocorrência do grupo",
x = "Longitude", y = "Latitude"
) +
theme(
panel.background = element_rect(fill = "#EBF5FB", colour = NA),
strip.text = element_text(size = 7, face = "bold"),
legend.position = "bottom",
axis.text = element_text(size = 6)
)celulas_com_ocorr <- df |> distinct(era5_lat, era5_lon)
dias_com <- df |>
filter(!is.na(tp_d)) |>
distinct(era5_lat, era5_lon, data_dia) |>
mutate(tem_ocorrencia = TRUE)
# clima_ref <- read_parquet(
# PATH_CLIMA,
# col_select = c("lat", "lon", "time",
# all_of(features_continuas),
# all_of(features_binarias))
# ) |>
# rename(era5_lat = lat, era5_lon = lon, data_dia = time) |>
# mutate(data_dia = as.Date(data_dia)) |>
# semi_join(celulas_com_ocorr, by = c("era5_lat", "era5_lon")) |>
# left_join(dias_com, by = c("era5_lat", "era5_lon", "data_dia")) |>
# mutate(tem_ocorrencia = replace_na(tem_ocorrencia, FALSE))
clima_ref <- read_parquet(
PATH_CLIMA,
col_select = c("lat", "lon", "time",
all_of(features_continuas),
all_of(features_binarias))
) |>
rename(era5_lat = lat, era5_lon = lon) |>
mutate(data_dia = as.Date(time)) |> # ← converter aqui, antes do join
select(-time) |>
semi_join(celulas_com_ocorr, by = c("era5_lat", "era5_lon")) |>
left_join(dias_com, by = c("era5_lat", "era5_lon", "data_dia")) |>
mutate(tem_ocorrencia = replace_na(tem_ocorrencia, FALSE))
cat(sprintf("Dias x celulas na referencia: %s\n",
format(nrow(clima_ref), big.mark = " ")))## Dias x celulas na referencia: 1 742 312
cat(sprintf(" Com ocorrencia : %s (%.1f%%)\n",
format(sum( clima_ref$tem_ocorrencia), big.mark = " "),
100 * mean( clima_ref$tem_ocorrencia)))## Com ocorrencia : 154 385 (8.9%)
cat(sprintf(" Sem ocorrencia : %s (%.1f%%)\n",
format(sum(!clima_ref$tem_ocorrencia), big.mark = " "),
100 * mean(!clima_ref$tem_ocorrencia)))## Sem ocorrencia : 1 587 927 (91.1%)
features_plot <- c(
"tp_d", "tp_acc_24h", "tp_acc_72h",
"t2m_max", "ws10_max", "amplitude_termica",
"n_dias_seco_prev", "rh_mean"
)
labs_features <- c(
tp_d = "Precipitação diária (mm)",
tp_acc_24h = "Precipitação acumulada 24h (mm)",
tp_acc_72h = "Precipitação acumulada 72h (mm)",
t2m_max = "Temperatura máxima (°C)",
ws10_max = "Velocidade máx. vento (m/s)",
amplitude_termica = "Amplitude térmica (°C)",
n_dias_seco_prev = "Dias secos anteriores",
rh_mean = "Humidade relativa média (%)"
)
for (feat in features_plot) {
cat(sprintf("\n\n### %s\n\n", labs_features[feat]))
p <- clima_ref |>
filter(
!is.na(.data[[feat]]),
.data[[feat]] < quantile(.data[[feat]], 0.99, na.rm = TRUE)
) |>
mutate(grupo = if_else(tem_ocorrencia, "Com ocorrência", "Sem ocorrência")) |>
ggplot(aes(x = grupo, y = .data[[feat]], fill = grupo)) +
geom_violin(alpha = 0.7, draw_quantiles = c(0.25, 0.5, 0.75)) +
geom_boxplot(width = 0.1, fill = "white", outlier.shape = NA) +
scale_fill_manual(values = c(
"Com ocorrência" = "#1F4E79",
"Sem ocorrência" = "#AAAAAA"
)) +
labs(title = labs_features[feat], x = NULL, y = labs_features[feat]) +
theme(legend.position = "none")
print(p)
cat("\n\n")
}freq_base <- clima_ref |>
summarise(across(all_of(features_binarias),
~ mean(.x == 1, na.rm = TRUE))) |>
pivot_longer(everything(), names_to = "feature", values_to = "freq_base")
freq_ocorr <- clima_ref |>
filter(tem_ocorrencia) |>
summarise(across(all_of(features_binarias),
~ mean(.x == 1, na.rm = TRUE))) |>
pivot_longer(everything(), names_to = "feature", values_to = "freq_ocorr")
risco <- freq_base |>
left_join(freq_ocorr, by = "feature") |>
mutate(
lift = freq_ocorr / freq_base,
feature_lab = labs_binarias[feature]
) |>
filter(!is.na(lift), !is.na(feature_lab))
risco |>
mutate(feature_lab = fct_reorder(feature_lab, lift)) |>
ggplot(aes(lift, feature_lab)) +
geom_col(aes(fill = lift > 1.5), show.legend = FALSE) +
geom_vline(xintercept = 1, linetype = "dashed", colour = "grey40") +
geom_text(aes(label = sprintf("%.2fx", lift)), hjust = -0.1, size = 3.2) +
scale_fill_manual(values = c("TRUE" = "#1F4E79", "FALSE" = "#AAAAAA")) +
scale_x_continuous(expand = expansion(mult = c(0, 0.15))) +
labs(
title = "Elevação de risco (lift) por evento climático extremo",
subtitle = "Rácio entre frequência do evento nos dias com ocorrência vs frequência base.\nValor > 1 indica sobre-representação de ocorrências nesses dias.",
x = "Lift (rácio de elevação)", y = NULL
)for (tipo in tipos_analisar) {
cat(sprintf("\n\n### %s\n\n",
str_trunc(tipo, width = 35, side = "right")))
dias_tipo <- df |>
filter(tipo_harmonizado == tipo, !is.na(tp_d)) |>
distinct(era5_lat, era5_lon, data_dia) |>
mutate(tem_tipo = TRUE)
ref_tipo <- clima_ref |>
left_join(dias_tipo, by = c("era5_lat", "era5_lon", "data_dia")) |>
mutate(tem_tipo = replace_na(tem_tipo, FALSE))
n_tipo <- sum(ref_tipo$tem_tipo)
lift_tipo <- features_binarias |>
map_dfr(function(feat) {
fb <- mean(ref_tipo[[feat]] == 1, na.rm = TRUE)
fo <- mean(ref_tipo[[feat]][ref_tipo$tem_tipo] == 1, na.rm = TRUE)
tibble(
feature = feat,
feature_lab = labs_binarias[feat],
lift = if_else(fb > 0, fo / fb, NA_real_)
)
}) |>
filter(!is.na(lift), !is.na(feature_lab))
titulo_plot <- str_wrap(sprintf("%s (n=%d)", tipo, n_tipo), width = 45)
p_lift <- lift_tipo |>
mutate(feature_lab = fct_reorder(feature_lab, lift)) |>
ggplot(aes(lift, feature_lab, fill = lift > 1.5)) +
geom_col(show.legend = FALSE) +
geom_vline(xintercept = 1, linetype = "dashed", colour = "grey40") +
geom_text(aes(label = sprintf("%.2fx", lift)), hjust = -0.1, size = 2.5) +
scale_fill_manual(values = c("TRUE" = "#1F4E79", "FALSE" = "#AAAAAA")) +
scale_x_continuous(expand = expansion(mult = c(0, 0.2))) +
labs(title = titulo_plot, subtitle = "Lift por evento extremo",
x = "Lift", y = NULL) +
theme(
plot.title = element_text(size = 8, face = "bold"),
axis.text.y = element_text(size = 8)
)
p_tp <- ref_tipo |>
filter(!is.na(tp_d),
tp_d < quantile(tp_d, 0.99, na.rm = TRUE)) |>
mutate(grupo = if_else(tem_tipo, "Com ocorrência", "Sem ocorrência")) |>
ggplot(aes(tp_d, fill = grupo, colour = grupo)) +
geom_density(alpha = 0.4) +
scale_fill_manual(values = c(
"Com ocorrência" = "#1F4E79",
"Sem ocorrência" = "#AAAAAA"
)) +
scale_colour_manual(values = c(
"Com ocorrência" = "#1F4E79",
"Sem ocorrência" = "#555555"
)) +
labs(title = "Precipitação diária (mm)",
x = "tp_d (mm/dia)", y = "Densidade",
fill = NULL, colour = NULL) +
theme(legend.position = "top",
plot.title = element_text(size = 9))
print(p_lift + p_tp + plot_layout(widths = c(2, 1)))
cat("\n\n")
}df_tipos |>
filter(!is.na(minutos_atraso), minutos_atraso > 0) |>
mutate(
condicao = case_when(
evento_precip_ext ~ "Precip extrema",
evento_vento_forte ~ "Vento forte",
evento_calor ~ "Calor extremo",
evento_gelo ~ "Gelo-degelo",
TRUE ~ "Sem evento extremo"
)
) |>
ggplot(aes(x = condicao, y = minutos_atraso,
fill = condicao, colour = condicao)) +
geom_violin(alpha = 0.5, draw_quantiles = 0.5, show.legend = FALSE) +
geom_boxplot(width = 0.15, fill = "white", outlier.shape = NA,
show.legend = FALSE) +
scale_y_log10(labels = comma) +
scale_fill_manual(values = c(
"Precip extrema" = "#1F4E79",
"Vento forte" = "#2E75B6",
"Calor extremo" = "#ED7D31",
"Gelo-degelo" = "#70AD47",
"Sem evento extremo" = "#AAAAAA"
)) +
scale_colour_manual(values = c(
"Precip extrema" = "#1F4E79",
"Vento forte" = "#2E75B6",
"Calor extremo" = "#ED7D31",
"Gelo-degelo" = "#70AD47",
"Sem evento extremo" = "#555555"
)) +
facet_wrap(~ tipo_harmonizado, scales = "free_y", ncol = 4) +
labs(
title = "Distribuição de minutos de atraso por condição climática e tipo",
subtitle = "Escala logarítmica — linha central = mediana",
x = NULL, y = "Minutos de atraso (log)"
) +
theme(
axis.text.x = element_text(angle = 35, hjust = 1, size = 7),
strip.text = element_text(size = 7)
)top15_seg <- df |> count(segmento) |>
slice_max(n, n = 15) |> pull(segmento)
top15_tipo <- df |> count(tipo_harmonizado) |>
slice_max(n, n = 15) |> pull(tipo_harmonizado)
df |>
filter(segmento %in% top15_seg, tipo_harmonizado %in% top15_tipo) |>
count(segmento, tipo_harmonizado) |>
mutate(segmento = fct_reorder(as.character(segmento), n, .fun = sum)) |>
ggplot(aes(tipo_harmonizado, segmento, fill = n)) +
geom_tile(colour = "white", linewidth = 0.4) +
geom_text(aes(label = n), size = 2.5, colour = "white") +
scale_fill_gradient(low = "#D6E4F0", high = "#1F4E79") +
scale_x_discrete(guide = guide_axis(angle = 40)) +
labs(
title = "Ocorrências por segmento x tipo (top 15 de cada)",
x = NULL, y = "Segmento", fill = "N.º ocorrências"
) +
theme(legend.position = "right",
axis.text.x = element_text(size = 7),
axis.text.y = element_text(size = 8))df |>
filter(segmento %in% top15_seg, !is.na(tp_d)) |>
mutate(segmento = as.character(segmento)) |>
ggplot(aes(
x = tp_d,
y = fct_reorder(segmento, tp_d, .fun = median),
fill = after_stat(x)
)) +
geom_density_ridges_gradient(scale = 2, rel_min_height = 0.01,
quantile_lines = TRUE, quantiles = 2) +
scale_fill_gradient(low = "#EBF5FB", high = "#1F4E79") +
scale_x_continuous(limits = c(0, 60)) +
labs(
title = "Distribuição de precipitação diária nas ocorrências por segmento",
subtitle = "Linha central = mediana | segmentos ordenados por mediana crescente",
x = "Precipitação diária (mm)", y = "Segmento", fill = "mm"
) +
theme(legend.position = "right",
axis.text.y = element_text(size = 8))limiares <- c(1, 5, 10, 20, 30, 50)
for (tipo in tipos_analisar[1:min(5, length(tipos_analisar))]) {
cat(sprintf("\n\n### %s\n\n",
str_trunc(tipo, width = 35, side = "right")))
segs_tipo <- df |>
filter(tipo_harmonizado == tipo, !is.na(tp_d)) |>
count(segmento) |>
filter(n >= 20) |>
pull(segmento)
if (length(segs_tipo) < 3) {
cat("*Segmentos insuficientes para análise (min. 3 com >= 20 ocorrências).*\n\n")
next
}
# Calcular % acima de cada limiar — sem map_dfc para evitar erros de scope
sens_limiares <- df |>
filter(tipo_harmonizado == tipo,
segmento %in% segs_tipo,
!is.na(tp_d)) |>
group_by(segmento) |>
summarise(
n_total = n(),
pct_acima_1mm = mean(tp_d >= 1, na.rm = TRUE),
pct_acima_5mm = mean(tp_d >= 5, na.rm = TRUE),
pct_acima_10mm = mean(tp_d >= 10, na.rm = TRUE),
pct_acima_20mm = mean(tp_d >= 20, na.rm = TRUE),
pct_acima_30mm = mean(tp_d >= 30, na.rm = TRUE),
pct_acima_50mm = mean(tp_d >= 50, na.rm = TRUE),
.groups = "drop"
) |>
pivot_longer(
starts_with("pct_acima"),
names_to = "limiar",
values_to = "pct_ocorr_acima"
) |>
mutate(
limiar_mm = as.numeric(str_extract(limiar, "\\d+")),
segmento = as.character(segmento)
)
p <- sens_limiares |>
ggplot(aes(limiar_mm, pct_ocorr_acima,
colour = segmento, group = segmento)) +
geom_line(linewidth = 1) +
geom_point(size = 2.5) +
scale_y_continuous(labels = percent_format(accuracy = 1),
limits = c(0, NA)) +
scale_x_continuous(breaks = limiares) +
scale_colour_manual(
values = colorRampPalette(cores_principais)(length(segs_tipo))
) +
labs(
title = str_wrap(sprintf("Curvas de sensibilidade — %s", tipo), 60),
subtitle = "% de ocorrências do tipo neste segmento em dias com tp >= limiar.\nCurvas que caem mais lentamente = segmentos mais sensíveis.",
x = "Limiar de precipitação (mm/dia)",
y = "% ocorrências acima do limiar",
colour = "Segmento"
) +
theme(legend.position = "right",
plot.title = element_text(size = 10))
print(p)
cat("\n\n")
}mat_cor <- df |>
select(all_of(features_continuas)) |>
filter(complete.cases(across(everything()))) |>
cor(method = "spearman")
rownames(mat_cor) <- colnames(mat_cor) <- recode(
rownames(mat_cor),
tp_d = "Precip diária",
tp_h_max = "Precip max/h",
tp_acc_3h = "Acum 3h",
tp_acc_6h = "Acum 6h",
tp_acc_12h = "Acum 12h",
tp_acc_24h = "Acum 24h",
tp_acc_48h = "Acum 48h",
tp_acc_72h = "Acum 72h",
t2m_min = "T min",
t2m_max = "T max",
t2m_mean = "T media",
amplitude_termica = "Amplitude T",
ws10_max = "Vento max",
ws10_mean = "Vento medio",
rh_min = "HR min",
rh_max = "HR max",
rh_mean = "HR media",
indice_calor = "Indice calor",
n_dias_seco_prev = "Dias secos ant."
)
corrplot(
mat_cor,
method = "color",
type = "upper",
order = "hclust",
col = colorRampPalette(c("#1F4E79", "white", "#C00000"))(200),
tl.cex = 0.8,
tl.col = "black",
addCoef.col = "black",
number.cex = 0.55,
diag = FALSE,
title = "Correlação de Spearman entre features climáticas",
mar = c(0, 0, 1.5, 0)
)Nota de modelação: grupos de features com correlação > 0,8 são redundantes em modelos lineares. Para tree-based models a colinearidade é menos crítica mas dificulta a interpretação de importâncias.
resumo_modelacao <- tipos_analisar |>
map_dfr(function(tipo) {
dias_tipo <- df |>
filter(tipo_harmonizado == tipo, !is.na(tp_d)) |>
distinct(era5_lat, era5_lon, data_dia) |>
mutate(tem_tipo = TRUE)
ref_t <- clima_ref |>
left_join(dias_tipo, by = c("era5_lat", "era5_lon", "data_dia")) |>
mutate(tem_tipo = replace_na(tem_tipo, FALSE))
n_ocorr <- sum(ref_t$tem_tipo)
n_segs <- df |> filter(tipo_harmonizado == tipo) |>
pull(segmento) |> n_distinct()
top_feat <- features_binarias |>
map_dfr(function(feat) {
fb <- mean(ref_t[[feat]] == 1, na.rm = TRUE)
fo <- mean(ref_t[[feat]][ref_t$tem_tipo] == 1, na.rm = TRUE)
tibble(feature = feat,
lift = if_else(fb > 0, fo / fb, NA_real_))
}) |>
filter(!is.na(lift)) |>
slice_max(lift, n = 1)
tibble(
tipo = tipo,
n_ocorrencias = n_ocorr,
n_segmentos = n_segs,
feature_top = labs_binarias[top_feat$feature],
lift_top = round(top_feat$lift, 2),
modelavel = n_ocorr >= MIN_OCORR_TIPO & top_feat$lift > 1.3
)
})
resumo_modelacao |>
arrange(desc(lift_top)) |>
mutate(modelavel = if_else(modelavel, "Sim", "")) |>
kable(
col.names = c("Tipo de Ocorrência", "N.º Ocorrências", "N.º Segmentos",
"Feature com maior lift", "Lift máx.", "Modelável"),
align = c("l", "r", "r", "l", "r", "c"),
booktabs = TRUE
) |>
kable_styling(
bootstrap_options = c("striped", "hover"),
latex_options = c("striped", "hold_position", "scale_down"),
full_width = TRUE
) |>
column_spec(6, bold = TRUE, color = "#1F4E79") |>
row_spec(which(resumo_modelacao$lift_top > 1.5), background = "#EBF5FB")| Tipo de Ocorrência | N.º Ocorrências | N.º Segmentos | Feature com maior lift | Lift máx. | Modelável |
|---|---|---|---|---|---|
| ESTRUTURAS | OCORRÊNCIA EM INSTALAÇÕES | 2817 | 48 | Vento >= 60 km/h | 35.68 | Sim |
| ESTRUTURAS | OCORRÊNCIA EM INSTALAÇÕES | 2817 | 48 | Chuva + vento forte | 35.68 | Sim |
| INSTALAÇÕES DE SINALIZAÇÃO EM PASSAGEM DE NÍVEL | 483 | 19 | Vento >= 60 km/h | 34.69 | Sim |
| INSTALAÇÕES DE SINALIZAÇÃO EM PASSAGEM DE NÍVEL | 483 | 19 | Chuva + vento forte | 34.69 | Sim |
| CONDIÇÕES METEREOLÓGICAS E CAUSAS NATURAIS | TEMPESTADES (CHUVAS TORRENCIAIS/VENTOS) | 232 | 18 | Precip > P99 | 26.45 | Sim |
| VIA | AVARIA MECÂNICA EM AMV | 1375 | 44 | Vento >= 60 km/h | 12.18 | Sim |
| VIA | AVARIA MECÂNICA EM AMV | 1375 | 44 | Chuva + vento forte | 12.18 | Sim |
| INSTALAÇÕES DE ALIMENTAÇÃO ELÉCTRICA | FALTA DE ALIMENTAÇÃO PELA EDP | 484 | 30 | Precip > P99 | 5.46 | Sim |
| INSTALAÇÕES DE SINALIZAÇÃO | AVARIA EM MESA DE COMANDO | 6932 | 45 | Vento >= 60 km/h | 4.83 | Sim |
| INSTALAÇÕES DE SINALIZAÇÃO | AVARIA EM MESA DE COMANDO | 6932 | 45 | Chuva + vento forte | 4.83 | Sim |
| INSTALAÇÕES DE SINALIZAÇÃO | AVARIA ELÉTRICA EM AMV | 13502 | 52 | Temp max >= 40°C | 3.73 | Sim |
| INSTALAÇÕES DE SINALIZAÇÃO | OCUPAÇÃO INTEMPESTIVA EM CIRCUITO VIA | 10937 | 53 | Vento >= 60 km/h | 3.06 | Sim |
| INSTALAÇÕES DE SINALIZAÇÃO | OCUPAÇÃO INTEMPESTIVA EM CIRCUITO VIA | 10937 | 53 | Chuva + vento forte | 3.06 | Sim |
| VIA | DEFICIÊNCIA DE VIA | 5596 | 47 | Temp max >= 40°C | 3.04 | Sim |
| INSTALAÇÕES DE ALIMENTAÇÃO ELÉCTRICA | DEFICIÊNCIA DE CATENÁRIA | 1007 | 45 | Onda de calor | 2.85 | Sim |
| FALHA NOS SISTEMAS DE APOIO À EXPLORAÇÃO | SDO(SISTEMA DE DETEÇÃO DE OBSTÁCULOS) | 1799 | 27 | Precip > P99 | 2.82 | Sim |
| INSTALAÇÕES DE ALIMENTAÇÃO ELÉCTRICA | DESARMES | 684 | 21 | Precip intensa pos-seca | 2.68 | Sim |
| FALHA NOS SISTEMAS DE APOIO À EXPLORAÇÃO | GAC | 548 | 29 | Precip intensa pos-seca | 2.51 | Sim |
| ESTRUTURAS | DEFICIÊNCIA EM OUTRAS INSTALAÇÕES FIXAS | 258 | 29 | Temp max >= 40°C | 2.35 | Sim |
| INSTALAÇÕES DE SINALIZAÇÃO | AVARIA EM SINAL | 21974 | 57 | Vento >= 60 km/h | 2.29 | Sim |
| INSTALAÇÕES DE SINALIZAÇÃO | AVARIA EM SINAL | 21974 | 57 | Chuva + vento forte | 2.29 | Sim |
| INSTALAÇÕES DE SINALIZAÇÃO EM PASSAGEM DE NÍVEL | AVARIA EM PN | 22372 | 35 | Vento >= 60 km/h | 2.25 | Sim |
| INSTALAÇÕES DE SINALIZAÇÃO EM PASSAGEM DE NÍVEL | AVARIA EM PN | 22372 | 35 | Chuva + vento forte | 2.25 | Sim |
| RISCOS / ACIDENTES / INCIDENTES PERIGOSOS | ATOS DE VANDALISMO | 7527 | 48 | Vento >= 60 km/h | 2.23 | Sim |
| RISCOS / ACIDENTES / INCIDENTES PERIGOSOS | ATOS DE VANDALISMO | 7527 | 48 | Chuva + vento forte | 2.23 | Sim |
| FALHA NOS SISTEMAS DE APOIO À EXPLORAÇÃO | TELECOMUNICAÇÕES | 4419 | 44 | Intensidade > 10mm/h | 1.95 | Sim |
| FALHA NOS SISTEMAS DE APOIO À EXPLORAÇÃO | SIP | 3840 | 27 | Intensidade > 10mm/h | 1.73 | Sim |
| INSTALAÇÕES DE SINALIZAÇÃO | AVARIA EM CIRCUITO VIA | 5678 | 50 | Precip > 20mm | 1.71 | Sim |
| RISCOS / ACIDENTES / INCIDENTES PERIGOSOS | FURTOS / ROUBOS / ASSALTOS | 1384 | 47 | Intensidade > 10mm/h | 1.70 | Sim |
| OUTRAS CAUSAS | AVARIA EM MATERIAL CIRCULANTE | 285 | 27 | Precip > 20mm | 1.68 | Sim |
| INSTALAÇÕES DE SINALIZAÇÃO | AVARIA/ANOMALIA EM CONTADOR DE EIXOS | 4183 | 31 | Intensidade > 10mm/h | 1.59 | Sim |
| PESSOAL | 464 | 28 | Onda de calor | 1.57 | Sim |
| FALHA NOS SISTEMAS DE APOIO À EXPLORAÇÃO | 511 | 24 | Intensidade > 10mm/h | 1.53 | Sim |
| INSTALAÇÕES DE SINALIZAÇÃO | CONVEL (IF) | 43273 | 44 | Onda de calor | 1.45 | Sim |
| IRREGULARIDADES NA EXECUÇÃO DE OBRAS | 4736 | 51 | Precip > 20mm | 1.43 | Sim |
| INSTALAÇÕES DE ALIMENTAÇÃO ELÉCTRICA | SOBRETENSÃO | 290 | 14 | Onda de calor | 1.42 | Sim |
| INSTALAÇÕES DE TELECOMUNICAÇÕES | 670 | 33 | Precip intensa pos-seca | 1.37 | Sim |
| PEDIDO DA EMPRESA FERROVIÁRIA | 4068 | 27 | Precip intensa pos-seca | 1.35 | Sim |
| ORGANIZAÇÃO DO HORÁRIO TÉCNICO | 2296 | 25 | Precip intensa pos-seca | 1.30 | |
| FALHA NOS SISTEMAS DE APOIO À EXPLORAÇÃO | PII (PROGRAMAÇÃO INFORMÁTICA ITINERÁRIOS) | 3149 | 24 | Onda de frio | 1.25 | |
| ERROS NOS PROCEDIMENTOS OPERACIONAIS | 2781 | 43 | Precip intensa pos-seca | 1.24 | |
| OUTRAS CAUSAS | 2398 | 34 | Precip intensa pos-seca | 1.24 | |
| FALHA NOS SISTEMAS DE APOIO À EXPLORAÇÃO | RADIO SOLO-COMBOIO | 2941 | 32 | Onda de frio | 1.18 | |
| FALHA NOS SISTEMAS DE APOIO À EXPLORAÇÃO | SATA | 213 | 17 | Onda de calor | 1.18 | |
| OBRAS PLANEADAS | 230 | 26 | Humidade > 90% | 1.16 |
Relatório gerado automaticamente em 06/03/2026 22:36 · Fase 4 EDA · Plano de Trabalhos 2026
# Instalar dependências se necessário
pkgs_necessarios <- c("rmarkdown", "tinytex", "knitr",
"sf", "rnaturalearth", "rnaturalearthdata")
pkgs_faltam <- pkgs_necessarios[!pkgs_necessarios %in% installed.packages()]
if (length(pkgs_faltam) > 0) install.packages(pkgs_faltam)
# Instalar TinyTeX se não tiver LaTeX instalado
if (!tinytex::is_tinytex() && Sys.which("xelatex") == "") {
tinytex::install_tinytex()
tinytex::tlmgr_install(c("booktabs", "longtable", "float",
"colortbl", "fancyhdr", "xcolor"))
}
rmd_path <- r"(\\refer.pt\rforfs01\DAM\AM-PR\AM-INF\Dados Climáticos Vs Ocorrências\eGOC_processed\eda_ocorrencias_clima.Rmd)"
# PDF
rmarkdown::render(
input = rmd_path,
output_format = "pdf_document",
output_file = "eda_ocorrencias_clima.pdf",
output_dir = dirname(rmd_path),
envir = new.env()
)
# HTML (mais interactivo — tabs, TOC flutuante)
rmarkdown::render(
input = rmd_path,
output_format = "html_document",
output_file = "eda_ocorrencias_clima.html",
output_dir = dirname(rmd_path),
envir = new.env()
)
message("Exportação concluída.")