Este relatório aplica Cadeias de Markov para analisar a evolução dos estados de risco associados aos Crimes Violentos Contra o Patrimônio (CVP) em Pernambuco. A base utilizada corresponde aos microdados disponibilizados pela Secretaria de Defesa Social de Pernambuco (SDS-PE), considerando o período de 2014 a 2025.
Ideia central: transformar a série histórica mensal de CVP em uma sequência de estados de risco e, a partir dessa sequência, estimar uma matriz de transição Markoviana.
O objetivo principal não é prever a quantidade exata de ocorrências, mas modelar a dinâmica probabilística de transição entre estados de risco. Nesta versão, utilizam-se cinco estados, o que torna a modelagem mais detalhada do que a versão com apenas três níveis.
Execute este bloco no console do RStudio apenas se os pacotes ainda não estiverem instalados.
A planilha deve estar na mesma pasta deste arquivo .Rmd
com o nome:
Microdados_CVP.xlsx
## [1] "data" "ano" "municipio"
## [4] "regiao_geografica" "total"
## Rows: 172,304
## Columns: 5
## $ data <dttm> 2014-01-01, 2014-01-01, 2014-01-01, 2014-01-01, 201…
## $ ano <dbl> 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014…
## $ municipio <chr> "ABREU E LIMA", "ARCOVERDE", "BELO JARDIM", "BONITO"…
## $ regiao_geografica <chr> "REGIÃO METROPOLITANA", "SERTÃO", "AGRESTE", "AGREST…
## $ total <dbl> 1, 3, 1, 1, 2, 2, 12, 1, 2, 2, 1, 10, 1, 7, 1, 3, 2,…
Nesta etapa, os dados são padronizados e filtrados para manter apenas o período de 2014 a 2025. O ano de 2026 foi excluído por representar um período incompleto.
dados <- dados %>%
mutate(
data = as.Date(data),
ano = year(data),
mes = month(data),
mes_nome = month(data, label = TRUE, abbr = TRUE),
ano_mes = floor_date(data, "month")
) %>%
filter(ano >= 2014, ano <= 2025)
resumo_base <- dados %>%
summarise(
total_registros = n(),
total_cvp = sum(total, na.rm = TRUE),
ano_inicial = min(ano),
ano_final = max(ano),
municipios = n_distinct(municipio),
regioes = n_distinct(regiao_geografica)
)
kable(resumo_base, caption = "Resumo geral da base utilizada")| total_registros | total_cvp | ano_inicial | ano_final | municipios | regioes |
|---|---|---|---|---|---|
| 168622 | 834404 | 2014 | 2025 | 187 | 5 |
Para aplicar Cadeias de Markov, os dados foram agregados mensalmente para todo o estado de Pernambuco.
cvp_mensal <- dados %>%
group_by(ano_mes) %>%
summarise(
total_cvp = sum(total, na.rm = TRUE),
.groups = "drop"
) %>%
arrange(ano_mes) %>%
mutate(
ano = year(ano_mes),
mes = month(ano_mes),
mes_nome = month(ano_mes, label = TRUE, abbr = TRUE)
)
kable(head(cvp_mensal, 12), caption = "Primeiros meses da série mensal de CVP")| ano_mes | total_cvp | ano | mes | mes_nome |
|---|---|---|---|---|
| 2014-01-01 | 4486 | 2014 | 1 | jan |
| 2014-02-01 | 4544 | 2014 | 2 | fev |
| 2014-03-01 | 5253 | 2014 | 3 | mar |
| 2014-04-01 | 5056 | 2014 | 4 | abr |
| 2014-05-01 | 6506 | 2014 | 5 | mai |
| 2014-06-01 | 5355 | 2014 | 6 | jun |
| 2014-07-01 | 5128 | 2014 | 7 | jul |
| 2014-08-01 | 5003 | 2014 | 8 | ago |
| 2014-09-01 | 5200 | 2014 | 9 | set |
| 2014-10-01 | 5614 | 2014 | 10 | out |
| 2014-11-01 | 5623 | 2014 | 11 | nov |
| 2014-12-01 | 5111 | 2014 | 12 | dez |
resumo_mensal <- cvp_mensal %>%
summarise(
minimo = min(total_cvp),
media = mean(total_cvp),
mediana = median(total_cvp),
maximo = max(total_cvp),
desvio_padrao = sd(total_cvp)
)
kable(resumo_mensal, digits = 2, caption = "Resumo estatístico dos totais mensais de CVP")| minimo | media | mediana | maximo | desvio_padrao |
|---|---|---|---|---|
| 2646 | 5794.47 | 5029.5 | 11046 | 2279.91 |
ggplot(cvp_mensal, aes(x = ano_mes, y = total_cvp)) +
geom_line(linewidth = 1) +
geom_point(size = 1.4) +
scale_y_continuous(labels = comma) +
labs(
title = "Evolução mensal dos CVP em Pernambuco",
subtitle = "Período de janeiro de 2014 a dezembro de 2025",
x = "Ano",
y = "Total mensal de CVP"
) +
theme_minimal()cvp_anual <- dados %>%
group_by(ano) %>%
summarise(total_cvp = sum(total, na.rm = TRUE), .groups = "drop")
kable(cvp_anual, caption = "Total anual de CVP em Pernambuco")| ano | total_cvp |
|---|---|
| 2014 | 62879 |
| 2015 | 81654 |
| 2016 | 112114 |
| 2017 | 120013 |
| 2018 | 94323 |
| 2019 | 79279 |
| 2020 | 52919 |
| 2021 | 51086 |
| 2022 | 50095 |
| 2023 | 45823 |
| 2024 | 44801 |
| 2025 | 39418 |
ggplot(cvp_anual, aes(x = factor(ano), y = total_cvp)) +
geom_col() +
geom_text(aes(label = comma(total_cvp)), vjust = -0.3, size = 3) +
scale_y_continuous(labels = comma, expand = expansion(mult = c(0, 0.12))) +
labs(
title = "Total anual de CVP em Pernambuco",
x = "Ano",
y = "Total anual de CVP"
) +
theme_minimal()top_municipios <- dados %>%
group_by(municipio) %>%
summarise(total_cvp = sum(total, na.rm = TRUE), .groups = "drop") %>%
arrange(desc(total_cvp)) %>%
slice_head(n = 10)
kable(top_municipios, caption = "Top 10 municípios com maior número de CVP")| municipio | total_cvp |
|---|---|
| RECIFE | 311876 |
| JABOATAO DOS GUARARAPES | 71084 |
| OLINDA | 62260 |
| CARUARU | 46878 |
| PAULISTA | 37306 |
| CABO DE SANTO AGOSTINHO | 21314 |
| PETROLINA | 19707 |
| CAMARAGIBE | 17879 |
| SANTA CRUZ DO CAPIBARIBE | 17363 |
| IGARASSU | 12926 |
ggplot(top_municipios, aes(x = reorder(municipio, total_cvp), y = total_cvp)) +
geom_col() +
geom_text(aes(label = comma(total_cvp)), hjust = -0.1, size = 3.4) +
coord_flip() +
scale_y_continuous(labels = comma, expand = expansion(mult = c(0, 0.18))) +
labs(
title = "Top 10 municípios com maior número de CVP",
x = "Município",
y = "Total de CVP"
) +
theme_minimal()cvp_regiao <- dados %>%
group_by(regiao_geografica) %>%
summarise(total_cvp = sum(total, na.rm = TRUE), .groups = "drop") %>%
arrange(desc(total_cvp))
kable(cvp_regiao, caption = "Distribuição dos CVP por região geográfica")| regiao_geografica | total_cvp |
|---|---|
| RECIFE | 311876 |
| REGIÃO METROPOLITANA | 256889 |
| AGRESTE | 154900 |
| ZONA DA MATA | 70495 |
| SERTÃO | 40244 |
ggplot(cvp_regiao, aes(x = reorder(regiao_geografica, total_cvp), y = total_cvp)) +
geom_col() +
geom_text(aes(label = comma(total_cvp)), hjust = -0.1, size = 3.4) +
coord_flip() +
scale_y_continuous(labels = comma, expand = expansion(mult = c(0, 0.15))) +
labs(
title = "Distribuição dos CVP por região geográfica",
x = "Região geográfica",
y = "Total de CVP"
) +
theme_minimal()ggplot(cvp_mensal, aes(x = factor(ano), y = total_cvp)) +
geom_boxplot() +
scale_y_continuous(labels = comma) +
labs(
title = "Distribuição mensal dos CVP por ano",
x = "Ano",
y = "Total mensal de CVP"
) +
theme_minimal()Nesta versão, a série mensal foi dividida em cinco estados de risco, definidos por quintis da distribuição histórica dos totais mensais de CVP. Essa estratégia evita limites arbitrários e utiliza a própria estrutura da base para criar os estados.
limites_5 <- quantile(
cvp_mensal$total_cvp,
probs = c(0.20, 0.40, 0.60, 0.80),
na.rm = TRUE
)
cvp_mensal <- cvp_mensal %>%
mutate(
estado = case_when(
total_cvp <= limites_5[1] ~ "Muito Baixo",
total_cvp <= limites_5[2] ~ "Baixo",
total_cvp <= limites_5[3] ~ "Medio",
total_cvp <= limites_5[4] ~ "Alto",
TRUE ~ "Muito Alto"
),
estado = factor(
estado,
levels = c("Muito Baixo", "Baixo", "Medio", "Alto", "Muito Alto")
)
)
tabela_limites_5 <- data.frame(
Estado = c("Muito Baixo", "Baixo", "Medio", "Alto", "Muito Alto"),
Criterio = c(
paste0("Até ", round(limites_5[1], 0), " ocorrências mensais"),
paste0("De ", round(limites_5[1] + 1, 0), " até ", round(limites_5[2], 0), " ocorrências mensais"),
paste0("De ", round(limites_5[2] + 1, 0), " até ", round(limites_5[3], 0), " ocorrências mensais"),
paste0("De ", round(limites_5[3] + 1, 0), " até ", round(limites_5[4], 0), " ocorrências mensais"),
paste0("Acima de ", round(limites_5[4], 0), " ocorrências mensais")
)
)
kable(tabela_limites_5, caption = "Critérios de classificação dos cinco estados de risco")| Estado | Criterio |
|---|---|
| Muito Baixo | Até 3807 ocorrências mensais |
| Baixo | De 3808 até 4366 ocorrências mensais |
| Medio | De 4367 até 5866 ocorrências mensais |
| Alto | De 5867 até 7953 ocorrências mensais |
| Muito Alto | Acima de 7953 ocorrências mensais |
A escolha por cinco estados permite uma leitura mais granular da dinâmica de risco, diferenciando períodos muito baixos, intermediários e muito altos de criminalidade patrimonial.
dist_estados <- cvp_mensal %>%
count(estado) %>%
mutate(percentual = n / sum(n) * 100)
kable(dist_estados, digits = 2, caption = "Distribuição dos meses por estado de risco")| estado | n | percentual |
|---|---|---|
| Muito Baixo | 29 | 20.14 |
| Baixo | 29 | 20.14 |
| Medio | 28 | 19.44 |
| Alto | 29 | 20.14 |
| Muito Alto | 29 | 20.14 |
ggplot(dist_estados, aes(x = estado, y = n)) +
geom_col() +
geom_text(aes(label = paste0(n, " (", round(percentual, 1), "%)")), vjust = -0.3) +
labs(
title = "Quantidade de meses por estado de risco",
x = "Estado de risco",
y = "Quantidade de meses"
) +
theme_minimal()ggplot(cvp_mensal, aes(x = ano_mes, y = total_cvp)) +
geom_line(linewidth = 1) +
geom_point(aes(shape = estado), size = 2) +
scale_y_continuous(labels = comma) +
labs(
title = "Evolução mensal dos CVP com estados de risco",
subtitle = "Classificação baseada em quintis da distribuição mensal",
x = "Ano",
y = "Total mensal de CVP",
shape = "Estado de risco"
) +
theme_minimal()Este gráfico facilita a visualização dos períodos em que cada ano concentrou meses em estados mais baixos ou mais altos de risco.
ggplot(cvp_mensal, aes(x = mes_nome, y = factor(ano), fill = estado)) +
geom_tile(color = "white") +
labs(
title = "Mapa temporal dos estados de risco por mês e ano",
x = "Mês",
y = "Ano",
fill = "Estado"
) +
theme_minimal()A Cadeia de Markov foi construída a partir da sequência temporal mensal dos estados de risco. Cada mês representa um estado observado, e a matriz de transição estima a probabilidade de mudança para o estado do mês seguinte.
sequencia <- as.character(cvp_mensal$estado)
modelo_markov <- markovchainFit(data = sequencia)
cadeia <- modelo_markov$estimate
cadeia## MLE Fit
## A 5 - dimensional discrete Markov Chain defined by the following states:
## Alto, Baixo, Medio, Muito Alto, Muito Baixo
## The transition matrix (by rows) is defined as follows:
## Alto Baixo Medio Muito Alto Muito Baixo
## Alto 0.75862069 0.0000000 0.17241379 0.06896552 0.00000000
## Baixo 0.00000000 0.4827586 0.20689655 0.00000000 0.31034483
## Medio 0.17857143 0.2857143 0.50000000 0.00000000 0.03571429
## Muito Alto 0.06896552 0.0000000 0.00000000 0.93103448 0.00000000
## Muito Baixo 0.00000000 0.2500000 0.07142857 0.00000000 0.67857143
matriz_transicao <- round(cadeia@transitionMatrix, 4)
kable(
matriz_transicao,
caption = "Matriz de transição da Cadeia de Markov com cinco estados"
)| Alto | Baixo | Medio | Muito Alto | Muito Baixo | |
|---|---|---|---|---|---|
| Alto | 0.7586 | 0.0000 | 0.1724 | 0.069 | 0.0000 |
| Baixo | 0.0000 | 0.4828 | 0.2069 | 0.000 | 0.3103 |
| Medio | 0.1786 | 0.2857 | 0.5000 | 0.000 | 0.0357 |
| Muito Alto | 0.0690 | 0.0000 | 0.0000 | 0.931 | 0.0000 |
| Muito Baixo | 0.0000 | 0.2500 | 0.0714 | 0.000 | 0.6786 |
Cada linha da matriz representa o estado atual, enquanto cada coluna representa o próximo estado. Assim, cada célula indica a probabilidade de transição de um estado para outro no mês seguinte.
matriz_df <- as.data.frame(as.table(cadeia@transitionMatrix))
colnames(matriz_df) <- c("Estado_Atual", "Proximo_Estado", "Probabilidade")
ggplot(matriz_df, aes(x = Proximo_Estado, y = Estado_Atual, fill = Probabilidade)) +
geom_tile(color = "white") +
geom_text(aes(label = sprintf("%.2f", Probabilidade)), size = 4) +
labs(
title = "Heatmap da matriz de transição dos estados de risco",
x = "Próximo estado",
y = "Estado atual",
fill = "Probabilidade"
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 35, hjust = 1))Além da matriz, é útil observar as transições mais prováveis, pois elas indicam os caminhos dominantes da dinâmica temporal dos CVP.
transicoes_top <- matriz_df %>%
arrange(desc(Probabilidade)) %>%
slice_head(n = 10) %>%
mutate(transicao = paste(Estado_Atual, "→", Proximo_Estado))
kable(transicoes_top, digits = 4, caption = "Dez transições mais prováveis da Cadeia de Markov")| Estado_Atual | Proximo_Estado | Probabilidade | transicao |
|---|---|---|---|
| Muito Alto | Muito Alto | 0.9310 | Muito Alto → Muito Alto |
| Alto | Alto | 0.7586 | Alto → Alto |
| Muito Baixo | Muito Baixo | 0.6786 | Muito Baixo → Muito Baixo |
| Medio | Medio | 0.5000 | Medio → Medio |
| Baixo | Baixo | 0.4828 | Baixo → Baixo |
| Baixo | Muito Baixo | 0.3103 | Baixo → Muito Baixo |
| Medio | Baixo | 0.2857 | Medio → Baixo |
| Muito Baixo | Baixo | 0.2500 | Muito Baixo → Baixo |
| Baixo | Medio | 0.2069 | Baixo → Medio |
| Medio | Alto | 0.1786 | Medio → Alto |
ggplot(transicoes_top, aes(x = reorder(transicao, Probabilidade), y = Probabilidade)) +
geom_col() +
geom_text(aes(label = sprintf("%.2f", Probabilidade)), hjust = -0.1) +
coord_flip() +
scale_y_continuous(labels = percent_format(accuracy = 1), expand = expansion(mult = c(0, 0.15))) +
labs(
title = "Transições mais prováveis entre estados de risco",
x = "Transição",
y = "Probabilidade"
) +
theme_minimal()As probabilidades de permanência correspondem aos valores da diagonal principal da matriz de transição. Elas indicam a chance de o estado atual se repetir no mês seguinte.
prob_permanencia <- data.frame(
Estado = rownames(cadeia@transitionMatrix),
Probabilidade_Permanencia = diag(cadeia@transitionMatrix)
)
kable(
prob_permanencia,
digits = 4,
caption = "Probabilidade de permanência em cada estado"
)| Estado | Probabilidade_Permanencia | |
|---|---|---|
| Alto | Alto | 0.7586 |
| Baixo | Baixo | 0.4828 |
| Medio | Medio | 0.5000 |
| Muito Alto | Muito Alto | 0.9310 |
| Muito Baixo | Muito Baixo | 0.6786 |
ggplot(prob_permanencia, aes(x = reorder(Estado, Probabilidade_Permanencia), y = Probabilidade_Permanencia)) +
geom_col() +
geom_text(aes(label = percent(Probabilidade_Permanencia, accuracy = 0.1)), hjust = -0.1) +
coord_flip() +
scale_y_continuous(labels = percent, limits = c(0, 1)) +
labs(
title = "Probabilidade de permanência no mesmo estado",
x = "Estado de risco",
y = "Probabilidade"
) +
theme_minimal()A distribuição estacionária representa o comportamento esperado da cadeia no longo prazo, assumindo que o padrão de transições observado permaneça constante.
estacionaria <- steadyStates(cadeia)
estacionaria_df <- data.frame(
Estado = colnames(estacionaria),
Probabilidade = as.numeric(estacionaria[1, ])
)
kable(
estacionaria_df,
digits = 4,
caption = "Distribuição estacionária da Cadeia de Markov"
)| Estado | Probabilidade |
|---|---|
| Alto | 0.1912 |
| Baixo | 0.2098 |
| Medio | 0.1846 |
| Muito Alto | 0.1912 |
| Muito Baixo | 0.2231 |
ggplot(estacionaria_df, aes(x = reorder(Estado, Probabilidade), y = Probabilidade)) +
geom_col() +
geom_text(aes(label = percent(Probabilidade, accuracy = 0.1)), hjust = -0.1) +
coord_flip() +
scale_y_continuous(labels = percent, limits = c(0, max(estacionaria_df$Probabilidade) * 1.25)) +
labs(
title = "Distribuição estacionária dos estados de risco",
x = "Estado de risco",
y = "Probabilidade de longo prazo"
) +
theme_minimal()A simulação abaixo não deve ser interpretada como previsão determinística, mas como uma possível trajetória compatível com as probabilidades estimadas pela Cadeia de Markov.
set.seed(123)
ultimo_estado <- tail(sequencia, 1)
simulacao <- rmarkovchain(
n = 12,
object = cadeia,
t0 = ultimo_estado
)
simulacao_df <- data.frame(
Mes = factor(
paste("Mês", 1:12),
levels = paste("Mês", 1:12)
),
Estado_Previsto = factor(simulacao, levels = levels(cvp_mensal$estado))
)
kable(
simulacao_df,
caption = "Simulação dos estados de risco para os próximos 12 meses"
)| Mes | Estado_Previsto |
|---|---|
| Mês 1 | Muito Baixo |
| Mês 2 | Baixo |
| Mês 3 | Baixo |
| Mês 4 | Medio |
| Mês 5 | Alto |
| Mês 6 | Alto |
| Mês 7 | Alto |
| Mês 8 | Medio |
| Mês 9 | Baixo |
| Mês 10 | Baixo |
| Mês 11 | Medio |
| Mês 12 | Medio |
ggplot(simulacao_df, aes(x = Mes, y = Estado_Previsto, group = 1)) +
geom_line() +
geom_point(size = 3) +
labs(
title = "Trajetória simulada dos estados de risco para os próximos 12 meses",
x = "Horizonte de simulação",
y = "Estado previsto"
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 35, hjust = 1))A matriz de transição indica as probabilidades de mudança entre os estados de risco definidos para os CVP em Pernambuco. Os valores localizados na diagonal principal representam a probabilidade de permanência no mesmo estado no mês seguinte. Valores elevados na diagonal sugerem estabilidade temporal, enquanto valores fora da diagonal indicam mudanças entre níveis de risco.
A adoção de cinco estados permite observar nuances que poderiam ser ocultadas em uma classificação mais simples. Por exemplo, em vez de apenas identificar meses de baixo, médio e alto risco, o modelo distingue períodos de risco muito baixo e muito alto, tornando a análise mais adequada para identificar persistência em extremos da série histórica.
A distribuição estacionária fornece uma visão de longo prazo da cadeia. Ela representa a proporção esperada de meses em cada estado, caso o padrão observado entre 2014 e 2025 permaneça constante. Essa métrica é relevante porque permite avaliar se a dinâmica histórica tende a concentrar o sistema em estados mais baixos, intermediários ou elevados de risco.
Apesar de útil, o modelo possui limitações importantes:
A aplicação das Cadeias de Markov permitiu modelar a evolução dos estados de risco associados aos Crimes Violentos Contra o Patrimônio em Pernambuco. O modelo não tem como objetivo prever o número exato de ocorrências, mas sim representar a dinâmica probabilística dos níveis de risco ao longo do tempo.
Os resultados obtidos por meio da matriz de transição, das probabilidades de permanência, da distribuição estacionária e da simulação dos estados futuros fornecem evidências quantitativas sobre a estabilidade e a mudança dos estados de risco no período analisado.
Como trabalhos futuros, recomenda-se ampliar a análise por região geográfica, comparar diferentes granularidades temporais, testar Cadeias de Markov de ordem superior e incorporar variáveis socioeconômicas ao modelo.