Introdução

A Teoria dos Grafos, ramo da Matemática que utiliza conceitos fundamentais para representar elementos definidos como nós (ou vértices) e arestas (ou conexões), lida com estruturas abstratas de relações. Em termos práticos, fornece um conjunto de conceitos, modelos e algoritmos que são utilizados para a análise de redes.

Para análise de redes com pacotes do R, utiliza-se a estrutura de dados em pares do tipo “from, to”, que representa a conexão entre os elementos. Entretanto, técnicas de análise de rede também são aplicadas a matrizes de correlação. Conceitos como centralidade, motivo e estrutura comunitária podem ser empregados para manifestar as propriedades ocultas em um conjunto de dados.

Nesse caso, estabelecemos um limite ou limiar a partir do qual os valores pareados são filtrados, como método para criar uma rede a partir de uma matriz de correlação.

Objetivo

Utilizamos ferramentas de análise de rede para visualizar e destacar diferentes grupos de Unidades de Planejamento, de acordo com o Plano de Desenvolvimento Territorial do município de Juiz de Fora.

Recursos

A relação de Unidades de Planejamento do município de Juiz de Fora pode ser extraída da página web https://www.pjf.mg.gov.br/desenvolvimentodoterritorio/dados/rp_centro_oeste.php.

Bibliotecas

Carregamos as seguintes bibliotecas do R no RStudio:

library(tidyverse)
library (rvest)
library(ggplot2)
library(igraph)
library(tidygraph)
library(ggraph)
library(rlang)
library(rmdformats)

Diretório de trabalho

Definimos o diretório de trabalho:

setwd("/home/gf/Scripts/Sisplan")

Coleta e limpeza de dados

Utilizamos a função read_html() do pacote rvest para ler a página e extrair os links relevantes:

url <- "https://www.pjf.mg.gov.br/desenvolvimentodoterritorio/dados/rp_centro_oeste.php"
page <- read_html(url)

rp <- page %>%
  html_nodes(xpath = "//li[@class='sub-sub']//a[contains(@href, '/rp')]") %>%
  html_attr("href")

Após a extração dos links, utilizamos a função lapply() para percorrer cada link e extrair as tabelas:

tbls <- lapply(rp, function(i) {
  z <- xml2::read_html(i) %>%
    html_nodes("table") %>%
    .[2] %>%
    html_table(fill = TRUE)
})

df <- bind_rows(tbls)

Feito isto, realizamos algumas transformações nos dados, como renomear colunas e calcular novas variáveis:

novos_nomes <- c("RP", "UP", "Localidades", "Hectares", "População", "Densidade")  
names(df) <- novos_nomes  

df <- df %>%
  mutate(Quant_Loc = str_count(Localidades, ",") + 1)

replan <- df %>%
  mutate(
    Hect = gsub("\\.", "", Hectares),
    Hectares = as.numeric(gsub(",", ".", Hect)),
    População = as.numeric(gsub("\\.", "", População)),
    Densidade = as.numeric(gsub(",", ".", gsub("\\.", "", Densidade, fixed = TRUE)))
  )

Convertemos o objeto replan em um data frame e atribuímos rótulos às linhas:

replan <- as.data.frame(replan)
rownames(replan) <- replan$UP

Por fim, removemos as colunas indesejadas:

replan <- replan %>%
  select(-1, -2, -3, -8)

Definição da matriz de correlação

Definimos a matriz de correlação com a função cor() do pacote igraph, e estabelecemos o ponto de corte:

mat <- cor(t(replan[, c(1, 2:4)]))
mat[mat<0.995] <- 0

Definição da rede

Convertemos a matriz de correlação em um objeto com estrutura de rede para ser tratado pelo pacote tidygraph:

set.seed(1)
ntw <- as_tbl_graph(mat, directed = FALSE)

O objeto tbl_graph gerado pela função as_tbl_graph() é composto por duas tabelas; uma contém os nós e outra as conexões.

ntw
## # A tbl_graph: 38 nodes and 145 edges
## #
## # An undirected multigraph with 8 components
## #
## # Node Data: 38 × 1 (active)
##    name                    
##    <chr>                   
##  1 CO1-Remonta             
##  2 CO2-Francisco Bernardino
##  3 CO3-Morro do Sabão      
##  4 CO4-Cerâmica            
##  5 CO5-Limeira             
##  6 C1-Mariano Procópio     
##  7 C2-Centro               
##  8 C3-São Mateus           
##  9 L1-Vitorino Braga       
## 10 L2-Progresso            
## # ℹ 28 more rows
## #
## # Edge Data: 145 × 3
##    from    to weight
##   <int> <int>  <dbl>
## 1     1     1  1    
## 2     1    21  0.998
## 3     2     2  1    
## # ℹ 142 more rows

Visualização da rede

Utilizamos o pacote ggraph para visualizar a rede e destacar diferentes grupos de Unidades de Planejamento. O parâmetro layout aceita 13 tipos de estruturas gráficas para representação visual da rede, de modo que a aparência muda de acordo com o algoritmo escolhido.

ggraph(ntw, layout = 'kk') +
  geom_edge_link() +
  geom_node_point() +
  geom_node_text(aes(label = name), size = 2, repel = TRUE, max.overlaps=Inf) +
  theme_graph()

Inspeção inicial

Exploramos algumas informações básicas da rede em análise. O tamanho da rede, correspondente à quantidade de conexões, é dado pela função gsize():

gsize(ntw)
## [1] 145

A ordem da rede, correspondente à quantidade de nós, é dada pela função gorder():

gorder(ntw)
## [1] 38

Características dos nós

Antes de mais nada, removemos os nós isolados, se houver:

ntw <- ntw %>%
  activate(nodes) %>%
  filter(!node_is_isolated())

Centralidade

A mais reconhecida características dos nós é a centralidade.
Quanto maior o índice de centralidade, mais central é o .

Há diversas medidas de centralidade.
A primeira medida, denominada grau de centralidade e calculada pela função centrality_degree(), captura o número de relacionamentos de um .

nocd <- ntw %>%
  activate("nodes") %>%
  mutate(grau = centrality_degree())
nocd
## # A tbl_graph: 38 nodes and 145 edges
## #
## # An undirected multigraph with 8 components
## #
## # Node Data: 38 × 2 (active)
##    name                      grau
##    <chr>                    <dbl>
##  1 CO1-Remonta                  3
##  2 CO2-Francisco Bernardino    12
##  3 CO3-Morro do Sabão           4
##  4 CO4-Cerâmica                12
##  5 CO5-Limeira                  6
##  6 C1-Mariano Procópio         12
##  7 C2-Centro                   12
##  8 C3-São Mateus               14
##  9 L1-Vitorino Braga            4
## 10 L2-Progresso                 2
## # ℹ 28 more rows
## #
## # Edge Data: 145 × 3
##    from    to weight
##   <int> <int>  <dbl>
## 1     1     1  1    
## 2     1    21  0.998
## 3     2     2  1    
## # ℹ 142 more rows

Criamos um gráfico para visualizar o grau de centralidade dos nós:

ggraph(nocd, layout = 'kk') +
geom_edge_link() +
geom_node_point(aes(size = grau, colour = grau)) +
scale_color_continuous(guide = 'legend') +
theme_graph()

A segunda medida de centralidade, traduzida como intermediação, é fornecida pela função centrality_betweenness(), que captura o número de caminhos mais curtos que passam por um .

nob <- ntw %>%
  activate("nodes") %>%
  mutate(inter = centrality_betweenness())

Que também pode ser visualizada graficamente:

ggraph(nob, layout = 'fr') +
geom_edge_link() +
geom_node_point(aes(size = inter, colour = inter)) +
scale_color_continuous(guide = 'legend') +
theme_graph()

A centralidade de proximidade de um é calculada como o inverso da soma do comprimento dos caminhos mais curtos entre o e todos os outros nós da rede. Quanto mais central for um , mais próximo ele estará de todos os outros.

nob <- ntw %>%
  activate("nodes") %>%
  mutate(proximidade = centrality_closeness())

Que também pode ser visualizada graficamente:

ggraph(nob, layout = 'kk') +
geom_edge_link() +
geom_node_point(aes(size = proximidade, colour = proximidade)) +
scale_color_continuous(guide = 'legend') +
theme_graph()
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_point()`).

Características da rede

Podem ser calculadas algumas métricas para avaliação da rede:

Densidade - proporção das possíveis conexões existentes
Diâmetro - caminho mais curto e mais longo na rede
Distância - distância média entre dois nós
Transitividade - probabilidade dos nós adjacentes serem interconectados

Densidade <- igraph::edge_density(ntw)
Diametro <- with_graph(ntw, graph_diameter())
Distancia <- with_graph(ntw, graph_mean_dist())
Transitividade <- igraph::transitivity(ntw)

tibble(Densidade, Diametro, Distancia, Transitividade)
## # A tibble: 1 × 4
##   Densidade Diametro Distancia Transitividade
##       <dbl>    <dbl>     <dbl>          <dbl>
## 1     0.206        3      1.37          0.894

Estatísticas da rede

Exibimos um resumo contendo algumas medidas estatísticas, calculadas através da função summarise():

resumo <- nocd %>%
  activate(nodes) %>%
  as_tibble() %>%
  summarise(
    média = mean(grau),
    máximo = max(grau),
    mediana = median(grau)) 

resumo
## # A tibble: 1 × 3
##   média máximo mediana
##   <dbl>  <dbl>   <dbl>
## 1  7.63     14       8

Função auxiliar

Com ajuda do pacote rlang, definimos uma função auxiliar, que recebe como entrada o data frame e a coluna escolhida como fator.

fator <- function(fonte, coluna) {
  coluna_nome <- sub(".*\\$", "", deparse(substitute(coluna)))
  coluna_quo <- enquo(coluna)
  
  fact <- tibble(
    name = rownames({{fonte}}),
    !!paste0("fct.", coluna_nome) := as.factor(!!coluna_quo)
  )
  
  return(list(fact, paste0("fct.", coluna_nome)))
}

Na chamada da função, atribuímos os argumentos de entrada desejados:

result <- fator(replan, replan$Quant_Loc)

A saída da função, que retorna tanto o data frame quanto o nome da coluna fatorizados, é armazenada em um objeto de nome “results”.

rp.group <- result[[1]]
coluna_fatorizada <- result[[2]]

Preparamos o grafo ntw para visualização, adicionando informações dos nós através de uma junção à esquerda (left join) com o data frame rp.group e renomeando a coluna resultante como “label”, que será usada para rotular os nós na visualização do grafo.

Como os objetos tbl_graph possuem duas tabelas, especificamos qual delas será modificada com a função activate().

ntw <- ntw %>%
  activate(nodes) %>%
  left_join(rp.group, by = "name") %>%
  dplyr::rename(label = name)

O nome da coluna fatorizada pode ser atribuído dinamicamente ao argumento color da função geom_node_point(), para especificar a cor dos nós, ao gerar o gráfico com o pacote ggraph.

Utilizamos o operador “!! sym” para converter a string do nome da coluna em um símbolo inteligível pela função ‘aes()’ do ggplot2.

ggraph(ntw, layout = 'kk') +
  geom_edge_link(aes(width = weight), alpha = 0.2) +
  scale_edge_width(range = c(0.2, 1)) +
  geom_node_point(aes(color = !!sym(coluna_fatorizada)), size = 2) +  
  geom_node_text(aes(label = label), size = 2, repel = TRUE, max.overlaps = Inf) +
  theme_graph() +
  guides(color = guide_legend(ncol = 2)) 

Detecção de comunidades

Finalmente, utilizamos uma função de agrupamento predefinida para identificar comunidades representadas por grupos de nós densamente conectados.

ntw %>%
  activate(nodes) %>%
  mutate(community = as.factor(group_infomap())) %>%
  ggraph(layout = "graphopt") +
  geom_edge_link(width = 1, colour = "lightgray") +
  geom_node_point(aes(colour = community), size = 4) +
  geom_node_text(aes(label = label), size = 2, repel = TRUE, max.overlaps=Inf) +
  theme_graph()

Referências:

Brazil, Noli (2023). Lab 9: Social Network Analysis. CRD 230 - Spatial Methods in Community Research. Acessado em 17/04/2024: https://crd230.github.io/lab9.html#Network_characteristics.

Brunson, Cory (2019). embed with the Petersens. Acessado em 17/04/2024: https://corybrunson.github.io/2019/03/01/embeddability/

Christian (2020). Graphs are fun: A gentle introduction to graphs in R. Statistics, Science, Random Ramblings. A blog mostly about data and R. Acessado em 17/04/2024: https://hohenfeld.is/posts/graphs-are-fun-an-introduction-to-graphs-in-r/

Hevey, David (2018). Network analysis: a brief overview and tutorial. HEALTH PSYCHOLOGY AND BEHAVIORAL MEDICINE. 2018, VOL. 6, NO. 1, 301–328. https://doi.org/10.1080/21642850.2018.1521283

Newman, M. E. J. (2006). Modularity and community structure in networks. PNAS. June 6. 2006. Vol. 103, n° 23, pg. 8577-8582. https://www.pnas.org/doi/epdf/10.1073/pnas.0601602103

Nowak, Benjamin (2021). [R] Network analysis with {tidygraph}. Acessado em 17/04/2024: https://bjnnowak.netlify.app/2021/09/30/r-network-analysis-with-tidygraph/

Pedersen, Thomas L. (2017). Introduction to ggraph: Layouts. Data Imaginist. Acessado em 17/04/2024: https://www.data-imaginist.com/posts/2017-02-06-ggraph-introduction-layouts/

Radicchi, F., Castellano, C., Cecconi, F., Loreto, V., & Parisi, D. (2004). Defining and identifying communities in networks. Proceedings of the national academy of sciences, 101(9), 2658-2663. https://www.pnas.org/doi/full/10.1073/pnas.0400054101

Sadamori, K., Naoki, M. (2019). Constructing networks by filtering correlation matrices: a null model approach. Proc. R. Soc. A.47520190578. http://doi.org/10.1098/rspa.2019.0578

Sadler, Jesse (2017). Introduction to Network Analysis with R. Creating static and interactive network graphs. Acessado em 17/04/2024: https://www.jessesadler.com/post/network-analysis-with-r/

Schweinberger, Martin(2024). Network Analysis using R. Brisbane: The University of Queensland. url: https://ladal.edu.au/net.html (Version 2024.03.02). Acessado em 17/04/2024: https://ladal.edu.au/net.html#Network_Statistics

Wilson, Robin J. (1996). Introduction to Graph Theory. Fourth edition. Longman. First published by Oliver & Boyd, 1972. Disponível em: https://www.maths.ed.ac.uk/~v1ranick/papers/wilsongraph.pdf