Roadmap

  1. Pacotes necessários
  2. Construção da rede real
  3. Cálculo de métricas estruturais clássicas
  4. Agrupamento e comunidades
  5. Conectividade da rede
  6. Identificando nós críticos
  7. Simulação de falha estrutural
  8. Estratégia de reforço
  9. Comparação com redes sintéticas
  10. Perfis + PCA + clustering
  11. Inspecionar arestas ligadas ao nó

1. Pacotes necessários

# ────────────────────────────────────────
library(igraph)
## 
## Attaching package: 'igraph'
## The following objects are masked from 'package:stats':
## 
##     decompose, spectrum
## The following object is masked from 'package:base':
## 
##     union
library(ggplot2)
set.seed(123)  # para reprodutibilidade

2. Construção da rede real (fictícia)

# ────────────────────────────────────────
# Criamos uma rede com nós e arestas representando conexões diversas
rotas <- matrix(c(
  "A", "B",  # A: 1
  "A", "C",  # A: 2
  "A", "D",  # A: 3
  "A", "E",  # A: 4
  
  "B", "D",  # B: 2
  "B", "F",  # B: 3
  "B", "H",  # B: 4
  
  "C", "D",  # C: 2
  "C", "Y",  # C: 3
  "C", "Z",  # C: 4
  
  "D", "E",  # D: 4
  "D", "F",  # D: 5
  
  "E", "F",  # E: 3
  "E", "I",  # E: 4
  
  "F", "G",  # F: 4
  "F", "Z",  # F: 5
  
  "G", "H",  # G: 2
  
  "H", "I",  # H: 2
  
  "Y", "Z"   # Y: 1, Z: 3
), byrow = TRUE, ncol = 2)

g <- graph_from_edgelist(rotas, directed = FALSE)

Criando um identificador único para cada aresta

E(g)$id <- paste0("e", seq_along(E(g)))

##definir manualmente os atributos X e Y nos nós

V(g)$X <- runif(vcount(g), min = -51.20364, max = -51.18838)  # Longitude
V(g)$Y <- runif(vcount(g), min = -30.03472, max = -30.02750)  # Latitude

Atributos aleatórios nas arestas

E(g)$faixas <- sample(1:5, ecount(g), replace = TRUE)                     # número de faixas (1 a 5)
E(g)$tem_semaforo <- sample(c(0, 1), ecount(g), replace = TRUE)          # binário: tem ou não
E(g)$comprimento_km <- runif(ecount(g), min = 0.1, max = 3.5)            # em km
E(g)$densidade_edif <- rnorm(ecount(g), mean = 0.5, sd = 0.1)            # densidade simulada

Visualização inicial da rede

plot(g, vertex.size = 30, vertex.color = "lightblue", main = "Rede Original")

data.frame(
  id = V(g)$name,
  X = V(g)$X,
  Y = V(g)$Y
)
##    id         X         Y
## 1   A -51.19925 -30.03145
## 2   B -51.19161 -30.02983
## 3   C -51.19740 -30.03059
## 4   D -51.19017 -30.03398
## 5   E -51.18929 -30.02822
## 6   F -51.20294 -30.03294
## 7   H -51.19558 -30.03442
## 8   Y -51.19002 -30.03235
## 9   Z -51.19523 -30.02783
## 10  I -51.19667 -30.02830
## 11  G -51.18904 -30.02972

Criando o resumo das arestas

df_arestas <- data.frame(
  id = E(g)$id,
  from = ends(g, E(g))[, 1],
  to   = ends(g, E(g))[, 2],
  faixas = E(g)$faixas,
  tem_semaforo = E(g)$tem_semaforo,
  comprimento_km = E(g)$comprimento_km,
  densidade_edif = E(g)$densidade_edif
)
df_arestas
##     id from to faixas tem_semaforo comprimento_km densidade_edif
## 1   e1    A  B      1            1      1.5954277      0.6207962
## 2   e2    A  C      1            0      2.6652155      0.3876891
## 3   e3    A  D      5            0      2.2393518      0.4597115
## 4   e4    A  E      3            0      2.5146202      0.4533345
## 5   e5    B  D      2            0      0.1021242      0.5779965
## 6   e6    B  F      2            1      1.7160764      0.4916631
## 7   e7    B  H      1            1      0.8484042      0.5253319
## 8   e8    C  D      3            0      1.3913762      0.4971453
## 9   e9    C  Y      4            1      2.1834214      0.4957130
## 10 e10    C  Z      1            0      1.2961129      0.6368602
## 11 e11    D  E      3            0      0.4778604      0.4774229
## 12 e12    D  F      5            1      0.9283062      0.6516471
## 13 e13    E  F      4            1      2.3713890      0.3451247
## 14 e14    E  I      2            0      1.5199991      0.5584614
## 15 e15    F  G      5            0      2.7798658      0.5123854
## 16 e16    F  Z      1            1      0.4497398      0.5215942
## 17 e17    H  G      1            0      1.5786353      0.5379639
## 18 e18    H  I      2            0      3.4488537      0.4497677
## 19 e19    Y  Z      3            0      3.1363738      0.4666793

3. Cálculo de métricas estruturais clássicas

🔹 Métricas de Centralidade

Grau: número de conexões

grau        <- degree(g)
grau
## A B C D E F H Y Z I G 
## 4 4 4 5 4 5 3 2 3 2 2

Betweenness: influência do nó como ponte entre pares

betweenness <- betweenness(g)
betweenness
##         A         B         C         D         E         F         H         Y 
##  3.583333  5.583333  6.000000  5.166667  6.583333 12.583333  2.750000  0.000000 
##         Z         I         G 
##  4.500000  1.000000  1.250000

Eigenvector: importância relativa do nó alavancada pelos vizinhos

eigen       <- eigen_centrality(g)$vector
eigen
##         A         B         C         D         E         F         H         Y 
## 0.8252319 0.7805574 0.6639840 1.0000000 0.7632768 0.8541215 0.3548072 0.2905100 
##         Z         I         G 
## 0.4652780 0.2876343 0.3110047

Closeness: proximidade média a todos os nós

closeness   <- closeness(g)
closeness
##          A          B          C          D          E          F          H 
## 0.05882353 0.05882353 0.05263158 0.06666667 0.05882353 0.06666667 0.04761905 
##          Y          Z          I          G 
## 0.04000000 0.05263158 0.04545455 0.04761905

###🔸 Transitividade (ou Fator de Agrupamento) Global:
- Mede a proporção de triângulos na rede inteira, comparado ao número de tríades (trios possíveis que poderiam formar triângulo).
- Avalia na rede como um todo, os amigos de amigos também são amigos?
- Mede a proporção de triângulos fechados em relação ao total de “triângulos possíveis” na rede como um todo.

agrupamento <- transitivity(g, type = "global", isolates = "zero")
agrupamento
## [1] 0.3396226

###🔹 Transitividade (ou Fator de Agrupamento) Local
cada nó, o quanto seus vizinhos estão interconectados entre si.
- Dos pares de vizinhos que um nó tem, quantos estão conectados entre si?
- Valor entre 0 e 1:
- 1 → todos os vizinhos do nó se conhecem (clique completo)
- 0 → nenhum vizinho se conhece

agrupamento <- transitivity(g, type = "local", isolates = "zero")
agrupamento
##         A         B         C         D         E         F         H         Y 
## 0.5000000 0.3333333 0.3333333 0.5000000 0.3333333 0.2000000 0.0000000 1.0000000 
##         Z         I         G 
## 0.3333333 0.0000000 0.0000000

Distâncias

Distância Média

dist_média  <- mean_distance(g)
dist_média
## [1] 1.890909

Diâmetro

diâmetro    <- diameter(g)
diâmetro
## [1] 4
cat("Distância média:", dist_média, "\n")
## Distância média: 1.890909
cat("Diâmetro:", diâmetro, "\n")
## Diâmetro: 4

4. Agrupamento e comunidades

No contexto da rede viária
- Alta transitividade local em uma interseção pode significar redundância e conectividade alternativa — o que pode ser bom para redistribuir fluxo em caso de congestionamento.
- Alta transitividade global sugere que a cidade, como um todo, tem alta interconectividade — característica de cidades planejadas ou com padrões mais regulares.

##🔹 Métricas de Agrupamento
### Transitividade global
Também conhecida como o coeficiente de agrupamento global (global clustering coefficient), ela mede a tendência da rede como um todo de formar triângulos — ou seja, de três nós quaisquer conectados dois a dois também estarem conectados entre si.
T_{global} =
- Valor próximo de 1 → A rede tem muitos triângulos → Forte coesão triádica → Muito agrupamento (ex: áreas urbanas densas).
- Valor próximo de 0 → Poucas conexões de terceiro nível → Rede mais linear ou hierárquica (ex: rodovias).

# ────────────────────────────────────────
transitivity(g, type = "global")
## [1] 0.3396226

Transitividade local

A transitividade local de um nó em uma rede representa o quanto os vizinhos desse nó estão conectados entre si. Essa métrica é útil para identificar agrupamentos locais ou malhas viárias densas, onde as conexões ao redor de uma interseção também se conectam entre si.
📘 Fórmula
A transitividade local C_i de um nó i é dada por:
\(C_i = \frac{2 \times e_i}{k_i (k_i - 1)}\)
Onde:
- e_i: número de arestas entre os vizinhos de i;
- k_i: grau (número de vizinhos) do nó i.
O resultado varia entre:
- 0 → Nenhuma conexão entre os vizinhos;
- 1 → Todos os vizinhos estão interconectados (formando um clique).

transitivity(g, type = "local")
##         A         B         C         D         E         F         H         Y 
## 0.5000000 0.3333333 0.3333333 0.5000000 0.3333333 0.2000000 0.0000000 1.0000000 
##         Z         I         G 
## 0.3333333 0.0000000 0.0000000

Calcular a transitividade local para todos os nós

trans_local <- transitivity(g, type = "local", isolates = "zero")

# Visualizar os valores

head(trans_local)
##         A         B         C         D         E         F 
## 0.5000000 0.3333333 0.3333333 0.5000000 0.3333333 0.2000000

comunidades ou walktrap

🔍 Walktrap
- Ele realiza passeios aleatórios curtos (por padrão, 4 passos) dentro da rede.
- Mede a similaridade entre nós com base nesses passeios.
- A partir disso, utiliza uma abordagem hierárquica para agrupar nós que frequentemente aparecem juntos nos passeios.
- O resultado é uma estrutura de comunidades que maximiza a modularidade da rede.
Detectar comunidades usando Walktrap

comunidades <- cluster_walktrap(g)
comunidades
## IGRAPH clustering walktrap, groups: 3, mod: 0.26
## + groups:
##   $`1`
##   [1] "A" "B" "D" "E" "F"
##   
##   $`2`
##   [1] "C" "Y" "Z"
##   
##   $`3`
##   [1] "H" "I" "G"
## 

Visualizar número de comunidades

length(comunidades)
## [1] 3

Acessar a qual comunidade pertence cada vértice

membership(comunidades)
## A B C D E F H Y Z I G 
## 1 1 2 1 1 1 3 2 2 3 3

Plotar graficamente

plot(comunidades, g, vertex.label = NA, vertex.size = 5)
📊 Por que usar o Walktrap?
- Robusto para grafos grandes e com estrutura modular bem definida.
- Não exige definir o número de comunidades previamente.
- Funciona bem para redes urbanas com zonas de conectividade local densa (ex: bairros bem conectados internamente mas com menos ligações externas).

plot(comunidades, g, main = "Comunidades Detectadas")

🧩 1. Juntando comunidades com os dados das ruas

Primeiro, vamos garantir que cada aresta (rua) saiba a qual comunidade pertencem seus nós de origem e destino:
Associe os grupos aos nós

comunidades <- cluster_walktrap(g)
membership_df <- data.frame(
  id = names(membership(comunidades)),
  comunidade = as.numeric(membership(comunidades))
)
membership_df
##    id comunidade
## 1   A          1
## 2   B          1
## 3   C          2
## 4   D          1
## 5   E          1
## 6   F          1
## 7   H          3
## 8   Y          2
## 9   Z          2
## 10  I          3
## 11  G          3

Juntando aos dados dos nós

library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:igraph':
## 
##     as_data_frame, groups, union
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
nodes <- data.frame(
  id = V(g)$name, 
  X = V(g)$X,  # se houver coordenadas
  Y = V(g)$Y
)
nodes_enriquecidos <- left_join(nodes, membership_df, by = "id")
Agora junte isso às arestas (edges), pegando os grupos dos dois extremos
rm(edges)
## Warning in rm(edges): objeto 'edges' não encontrado
class(g)
## [1] "igraph"
edges <- igraph::as_data_frame(g, what = "edges")
edges_vis <- edges %>%
  left_join(nodes_enriquecidos %>% dplyr::select(id, comunidade), by = c("from" = "id")) %>%
  rename(comunidade_from = comunidade) %>%
  left_join(nodes_enriquecidos %>% dplyr::select(id, comunidade), by = c("to" = "id")) %>%
  rename(comunidade_to = comunidade)

Criar uma coluna de comunidade dominante por rua (por simplicidade)

edges_vis$comunidade_rua <- ifelse(edges_vis$comunidade_from == edges_vis$comunidade_to,
                                   edges_vis$comunidade_from,
                                   NA)

🏙 Cruzando com os atributos urbanos

Agora, podemos fazer análises por comunidade, por exemplo:

library(dplyr)
# Agrupar por comunidade da rua e calcular média dos atributos urbanos
analise_comunidades <- edges_vis %>%
  filter(!is.na(comunidade_rua)) %>%
  group_by(comunidade_rua) %>%
  summarise(
    media_faixas = mean(faixas, na.rm = TRUE),
    prop_semaforo = mean(tem_semaforo, na.rm = TRUE),
    densidade_media = mean(densidade_edif, na.rm = TRUE),
    comprimento_medio = mean(comprimento_km, na.rm = TRUE)
  )

🌈 Visualizando comunidades urbanas com visNetwork ou mapview

Se quiser colorir o grafo pelas comunidades:

nodes_vis <- left_join(nodes, membership_df, by = "id")
nodes_vis$group <- as.factor(nodes_vis$comunidade)

library(visNetwork)
visNetwork(nodes_vis, edges) %>%
  visGroups(groupname = levels(nodes_vis$group)) %>%
  visOptions(highlightNearest = TRUE, nodesIdSelection = TRUE) %>%
  visLegend()
#Ou, se preferir um mapa interativo com cores por comunidade:
library(mapview)
library(sf)
## Linking to GEOS 3.11.2, GDAL 3.8.2, PROJ 9.3.1; sf_use_s2() is TRUE
library(dplyr)
library(purrr)
## 
## Attaching package: 'purrr'
## The following objects are masked from 'package:igraph':
## 
##     compose, simplify
# Junta as coordenadas dos nós de origem e destino
edges_sf <- edges_vis %>%
  left_join(nodes, by = c("from" = "id")) %>%
  rename(X_from = X, Y_from = Y) %>%
  left_join(nodes, by = c("to" = "id")) %>%
  rename(X_to = X, Y_to = Y) %>%
  rowwise() %>%
  mutate(geometry = st_sfc(
    st_linestring(matrix(c(X_from, X_to, Y_from, Y_to), ncol = 2)), crs = 4326
  )) %>%
  ungroup() %>%
  st_as_sf()
# Supondo que edges_vis tenha geometria em `sf`
mapview(edges_sf, zcol = "comunidade_rua", legend = TRUE)

📌 Próximos passos possíveis

  • Detectar comunidades com maior propensão ao congestionamento;
  • Mapear padrões espaciais urbanos distintos entre grupos;
  • Gerar um relatório com estatísticas por comunidade — como tipo de rua predominante ou nível de acessibilidade.

Modularidade

📐 Modularidade (Modularity)
É uma métrica que varia de -1 a 1 (na prática, geralmente entre 0 e 1), e avalia a qualidade da partição da rede em comunidades. A fórmula simplificada da modularidade Q é:
Q = {i,j} (c_i, c_j)
Onde:
- A
{ij}: valor da aresta entre os nós i e j;
- k_i, k_j: grau dos nós i e j;
- m: número total de arestas;
- (c_i, c_j): vale 1 se i e j estão na mesma comunidade, 0 se não.

Esse valor indica o quanto as conexões estão concentradas dentro
das comunidades, em comparação ao que se esperaria ao acaso (em uma rede com as mesmas distribuições de grau, mas conexões aleatórias).
📏 Escala de interpretação da modularidade
- Q ≤ 0:
🔴 Sem estrutura comunitária
A divisão da rede é pior ou igual à aleatoriedade. Comunidades não fazem sentido.
- 0 < Q < 0.3:
🟠 Comunidades fracas
Há uma leve preferência por conexões internas, mas ainda é um agrupamento difuso.
- 0.3 ≤ Q < 0.6:
🟡 Comunidades razoáveis a boas
Estrutura comunitária perceptível. Nós tendem a estar mais conectados dentro de seus grupos do que fora.
- 0.6 ≤ Q < 0.8:
🟢 Comunidades bem definidas
Forte modularidade, agrupamentos bem separados. Estrutura comunitária sólida.
- Q ≥ 0.8:
🔵 Comunidades muito bem formadas (raro)
Quase toda a conectividade está contida dentro das comunidades. Pode indicar estrutura fortemente compartimentalizada.

modularity(comunidades)
## [1] 0.2590028

5. Conectividade da rede

🔹 Métricas de Conectividade

Verifica se a rede é conexa

is_connected(g)
## [1] TRUE

Quantidade e tamanho dos componentes

components(g)
## $membership
## A B C D E F H Y Z I G 
## 1 1 1 1 1 1 1 1 1 1 1 
## 
## $csize
## [1] 11
## 
## $no
## [1] 1

Pontos de articulação

Pontos de articulação (ou articulation points) são nós críticos de uma rede — ou seja, vértices cuja remoção desconecta uma parte do grafo. Em outras palavras, eles são “gargalos” ou “portas” que mantêm diferentes regiões da rede conectadas.

articulation_points(g)
## + 0/11 vertices, named, from 46674d3:

🔎 Interpretação:

  • 0/11 vertices, named, from 9fb26fb:

Nenhum dos nós em g é crítico a ponto de, se removido, desconectar a rede. Em outras palavras, sua rede é resiliente: a remoção de qualquer nó não interrompe a conectividade geral entre os outros. Esse resultado é comum em redes:
- redundantes, com caminhos alternativos entre regiões;
- ou densamente conectadas, onde os nós têm múltiplas conexões paralelas.

articulation_points(g)
## + 0/11 vertices, named, from 46674d3:

6. Identificando nós críticos por betweenness

ranking <- sort(betweenness, decreasing = TRUE)
ranking
##         F         E         C         B         D         Z         A         H 
## 12.583333  6.583333  6.000000  5.583333  5.166667  4.500000  3.583333  2.750000 
##         G         I         Y 
##  1.250000  1.000000  0.000000
nos_críticos <- names(ranking)[1:3]
cat("Nós mais críticos:", paste(nos_críticos, collapse = ", "), "\n")
## Nós mais críticos: F, E, C

7. Simulação de falha estrutural

Simulação de falha — removendo os 3 nós mais críticos

g_colapsado <- delete_vertices(g, nos_críticos)
plot(g_colapsado, vertex.color = "gray", main = "Rede Após Falhas Críticas")

cat("Distância média pós-falha:", mean_distance(g_colapsado), "\n")
## Distância média pós-falha: 1.8125
cat("Rede conectada? ", is_connected(g_colapsado), "\n")
## Rede conectada?  FALSE
cat("Componentes presentes:", components(g_colapsado)$no, "\n")
## Componentes presentes: 2

8. Estratégia de reforço — reconectando a rede com novas arestas

v_validos <- V(g_colapsado)$name
arestas_novas <- matrix(c("C", "I", "D", "G", "A", "I"), byrow = TRUE, ncol = 2)

arestas_filtradas <- arestas_novas[
  apply(arestas_novas, 1, function(par) all(par %in% v_validos)),
]

if (nrow(arestas_filtradas) > 0) {
  g_reforçado <- add_edges(g_colapsado, t(arestas_filtradas))
} else {
  g_reforçado <- g_colapsado
  message("Nenhuma aresta válida foi encontrada — rede mantida como está.")
}

plot(g_reforçado, vertex.color = "lightgreen", main = "Rede Reforçada")

cat("Distância média pós-reforço:", mean_distance(g_reforçado), "\n")
## Distância média pós-reforço: 1.4375
cat("Rede reconectada? ", is_connected(g_reforçado), "\n")
## Rede reconectada?  FALSE

9. Comparação com redes sintéticas (referenciais teóricos)

Rede Original

n <- vcount(g)
m <- ecount(g)
k <- round(mean(grau))
plot(g)

Rede Erdos Renyi

g_er <- erdos.renyi.game(n, m, type = "gnm")   
plot(g_er)# Aleatória

Rede Watts Strogatz

g_ws <- watts.strogatz.game(1, size = n, nei = k %/% 2, p = 0.05)   # Small-world
## Warning: `watts.strogatz.game()` was deprecated in igraph 2.0.0.
## ℹ Please use `sample_smallworld()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
plot(g_ws)

Rede Barabasi

g_ba <- barabasi.game(n, m = k %/% 2)                               # Com hubs
## Warning: `barabasi.game()` was deprecated in igraph 2.0.0.
## ℹ Please use `sample_pa()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
plot(g_ba)

Comparativo entre redes

get_metrics <- function(gr) {
  c(
    dist_média  = mean_distance(gr),
    agrupamento = transitivity(gr, type = "global"),
    diâmetro    = diameter(gr),
    grau_médio  = mean(degree(gr))
  )
}

tabela_comp <- rbind(
  Real           = get_metrics(g),
  Erdős_Rényi    = get_metrics(g_er),
  Watts_Strogatz = get_metrics(g_ws),
  Barabási       = get_metrics(g_ba)
)

cat("\n🔎 Comparação entre estruturas:\n")
## 
## 🔎 Comparação entre estruturas:
print(round(tabela_comp, 3))
##                dist_média agrupamento diâmetro grau_médio
## Real                1.891       0.340        4      3.455
## Erdős_Rényi         1.909       0.508        3      3.455
## Watts_Strogatz      3.000       0.000        5      2.000
## Barabási            1.438       0.000        3      1.818

10. Perfis + PCA + clustering

Perfis dos nós e análise multivariada com PCA + k-means

perfil_nos <- data.frame(
  nome        = V(g)$name,
  grau        = grau,
  betweenness = betweenness,
  eigenvector = eigen,
  closeness   = closeness,
  clustering  = agrupamento
)
rownames(perfil_nos) <- perfil_nos$nome
perfil_nos$nome <- NULL

colunas_variaveis <- apply(perfil_nos, 2, sd) != 0
perfil_limp <- perfil_nos[, colunas_variaveis]

pca <- prcomp(perfil_limp, scale. = TRUE)
pca
## Standard deviations (1, .., p=5):
## [1] 1.9027813 1.0341225 0.5041325 0.2097583 0.1089300
## 
## Rotation (n x k) = (5 x 5):
##                     PC1         PC2         PC3         PC4         PC5
## grau        -0.51786419 -0.05074758  0.07441624 -0.71163639  0.46613005
## betweenness -0.46262112  0.21376016 -0.83055458  0.10419512 -0.19902453
## eigenvector -0.50435724 -0.16810957  0.40707440 -0.06315581 -0.74004373
## closeness   -0.51268034  0.06881850  0.28511117  0.68125672  0.43246247
## clustering  -0.02410441 -0.95850637 -0.24009032  0.12090347  0.09177963

🔢 Variância explicada

sdev <- pca$sdev
variancia <- sdev^2
variancia_perc <- variancia / sum(variancia) * 100
explicacao <- data.frame(
  Componente = paste0("PC", seq_along(sdev)),
  Desvio_Padrao = round(sdev, 3),
  Variancia = round(variancia, 3),
  Percentual = round(variancia_perc, 1)
)
print(explicacao)
##   Componente Desvio_Padrao Variancia Percentual
## 1        PC1         1.903     3.621       72.4
## 2        PC2         1.034     1.069       21.4
## 3        PC3         0.504     0.254        5.1
## 4        PC4         0.210     0.044        0.9
## 5        PC5         0.109     0.012        0.2

🧭 Loadings — o que cada componente está medindo

A tabela Rotation mostra os loadings (coeficientes) que ligam os componentes às variáveis originais. Veja como ler:
🔵 PC1
grau 0.51378
betweenness 0.49191
eigenvector 0.49423
closeness 0.49979
Todos os valores são positivos e próximos → PC1 representa uma média ponderada de TODAS as métricas de centralidade. Ou seja:
🧠 PC1 mede a centralidade “geral” de um nó: quanto mais central em qualquer sentido, maior a pontuação.

🟡 PC2 betweenness 0.65118 (muito alta)
grau 0.30902
closeness -0.39606
eigenvector -0.56886
Aqui temos um contraste: betweenness é positiva, enquanto closeness e eigenvector são negativas.
🧭 PC2 diferencia nós “articuladores” (alta betweenness) daqueles integrados localmente (alta eigen/closeness).
Nó com PC2 alto → atua como ponte Nó com PC2 baixo → atua mais como hub interno ou periférico
Componente | Interpretação prática
PC1 | Centralidade geral (todos os scores altos)
PC2 | Contraste entre ponte (betweenness) e hub local
PC3/4 | Detalhes marginais (pouco impacto global)

🔗 Clusterização com k-means

set.seed(123)
agrupamento <- kmeans(scale(perfil_limp), centers = 3)
perfil_nos$cluster <- as.factor(agrupamento$cluster)

df_pca <- as.data.frame(pca$x[, 1:2])
df_pca$cluster <- perfil_nos$cluster
df_pca$nome <- rownames(perfil_nos)

ggplot(df_pca, aes(x = PC1, y = PC2, color = cluster)) +
  geom_point(size = 4) +
  geom_text(aes(label = nome), vjust = -1.2, size = 3.5) +
  labs(title = "Clusters de Perfis Estruturais (PCA + k-means)", color = "Grupo") +
  theme_minimal()

11. Inspecionando as arestas conectadas a um nó

nó <- "E"
arestas_incidentes <- incident(g, nó, mode = "all")
ids_das_arestas <- E(g)[arestas_incidentes]$id
cat("\nArestas conectadas ao nó", nó, ":", paste(ids_das_arestas, collapse = ", "), "\n")
## 
## Arestas conectadas ao nó E : e4, e11, e13, e14
df_arestas
##     id from to faixas tem_semaforo comprimento_km densidade_edif
## 1   e1    A  B      1            1      1.5954277      0.6207962
## 2   e2    A  C      1            0      2.6652155      0.3876891
## 3   e3    A  D      5            0      2.2393518      0.4597115
## 4   e4    A  E      3            0      2.5146202      0.4533345
## 5   e5    B  D      2            0      0.1021242      0.5779965
## 6   e6    B  F      2            1      1.7160764      0.4916631
## 7   e7    B  H      1            1      0.8484042      0.5253319
## 8   e8    C  D      3            0      1.3913762      0.4971453
## 9   e9    C  Y      4            1      2.1834214      0.4957130
## 10 e10    C  Z      1            0      1.2961129      0.6368602
## 11 e11    D  E      3            0      0.4778604      0.4774229
## 12 e12    D  F      5            1      0.9283062      0.6516471
## 13 e13    E  F      4            1      2.3713890      0.3451247
## 14 e14    E  I      2            0      1.5199991      0.5584614
## 15 e15    F  G      5            0      2.7798658      0.5123854
## 16 e16    F  Z      1            1      0.4497398      0.5215942
## 17 e17    H  G      1            0      1.5786353      0.5379639
## 18 e18    H  I      2            0      3.4488537      0.4497677
## 19 e19    Y  Z      3            0      3.1363738      0.4666793
df_arestas <- igraph::as_data_frame(g, what = "edges")
df_arestas_conectadas <- df_arestas[as.numeric(arestas_incidentes), ]
df_arestas_conectadas$id <- ids_das_arestas
print(df_arestas_conectadas)
##    from to  id faixas tem_semaforo comprimento_km densidade_edif
## 4     A  E  e4      3            0      2.5146202      0.4533345
## 11    D  E e11      3            0      0.4778604      0.4774229
## 13    E  F e13      4            1      2.3713890      0.3451247
## 14    E  I e14      2            0      1.5199991      0.5584614