1 Introdução

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.

2 Instalação dos pacotes

Execute este bloco no console do RStudio apenas se os pacotes ainda não estiverem instalados.

options(repos = c(CRAN = "https://cloud.r-project.org"))

install.packages(c(
  "readxl",
  "dplyr",
  "ggplot2",
  "lubridate",
  "tidyr",
  "markovchain",
  "janitor",
  "knitr",
  "scales"
))

3 Carregamento dos pacotes

library(readxl)
library(dplyr)
library(ggplot2)
library(lubridate)
library(tidyr)
library(markovchain)
library(janitor)
library(knitr)
library(scales)

4 Importação dos dados

A planilha deve estar na mesma pasta deste arquivo .Rmd com o nome:

Microdados_CVP.xlsx
dados <- read_excel("Microdados_CVP.xlsx") %>%
  clean_names()

names(dados)
## [1] "data"              "ano"               "municipio"        
## [4] "regiao_geografica" "total"
glimpse(dados)
## 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,…

5 Preparação dos dados

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")
Resumo geral da base utilizada
total_registros total_cvp ano_inicial ano_final municipios regioes
168622 834404 2014 2025 187 5

6 Série mensal de CVP em Pernambuco

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")
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

7 Estatísticas descritivas

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")
Resumo estatístico dos totais mensais de CVP
minimo media mediana maximo desvio_padrao
2646 5794.47 5029.5 11046 2279.91

8 Evolução mensal dos CVP

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()

9 Evolução anual dos CVP

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")
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()

10 Top 10 municípios com maior número de ocorrências

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")
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()

11 Distribuição por região geográfica

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")
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()

12 Distribuição mensal por ano

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()

13 Definição dos estados de risco Markovianos

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")
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.

14 Distribuição dos estados de risco

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")
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()

15 Evolução dos estados de risco ao longo do tempo

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()

16 Mapa temporal dos estados de risco

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()

17 Aplicação das Cadeias de Markov

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

18 Matriz de transição

matriz_transicao <- round(cadeia@transitionMatrix, 4)

kable(
  matriz_transicao,
  caption = "Matriz de transição da Cadeia de Markov com cinco estados"
)
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.

19 Heatmap da matriz de transição

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))

20 Transições mais frequentes

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")
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()

21 Probabilidades de permanência

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"
)
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()

22 Distribuição estacionária

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"
)
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()

23 Simulação dos próximos 12 meses

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"
)
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))

24 Interpretação automática dos principais achados

Principal resultado da permanência: o estado com maior probabilidade de permanecer no mesmo nível no mês seguinte foi Muito Alto, com probabilidade de 93.1%.

Comportamento de longo prazo: de acordo com a distribuição estacionária, o estado com maior probabilidade no longo prazo foi Muito Baixo, com 22.3%.

25 Discussão dos resultados

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.

26 Limitações

Apesar de útil, o modelo possui limitações importantes:

  1. A Cadeia de Markov considera que o próximo estado depende apenas do estado atual.
  2. A modelagem utiliza apenas a série histórica dos totais mensais de CVP, sem incorporar variáveis socioeconômicas, demográficas ou operacionais.
  3. A simulação dos próximos meses representa uma trajetória probabilística, não uma previsão exata.
  4. A agregação estadual pode ocultar diferenças entre municípios e regiões geográficas.

27 Conclusão

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.