Eu tenho esta rede de grafos em R:
library(igraph)
n_rows <- 10
n_cols <- 5
g <- make_lattice(dimvector = c(n_cols, n_rows))
layout <- layout_on_grid(g, width = n_cols)
n_nodes <- vcount(g)
node_colors <- rep("white", n_nodes)
for (row in 0:(n_rows-1)) {
start_index <- row * n_cols + 1
node_colors[start_index:(start_index+2)] <- "orange"
node_colors[(start_index+3):(start_index+4)] <- "purple"
}
node_labels <- 1:n_nodes
plot(g,
layout = layout,
vertex.color = node_colors,
vertex.label = node_labels,
vertex.label.color = "black",
vertex.size = 15,
edge.color = "gray",
main = "Rectangular Undirected Network")
Estou tentando escrever uma função que divide aleatoriamente essa rede em 5 subgráficos conectados (ou seja, minigráficos) de modo que cada nó apareça exatamente uma vez.
Acho que, em teoria, isso não deve ser muito difícil de fazer. Eu precisaria identificar aleatoriamente um nó, decidir aleatoriamente quantos vizinhos incluir, selecionar esses vizinhos e removê-los do gráfico... e reiniciar esse processo no gráfico restante. Claro, alguns detalhes adicionais precisariam ser especificados, por exemplo, se o número aleatório especificado exceder o número de nós restantes, então use uma função max, BFS precisaria ser usado para selecionar os nós, etc.
Aqui está minha primeira tentativa de escrever o código:
get_connected_subgraph <- function(graph, available_nodes, min_nodes = 5, max_nodes = 15) {
if (length(available_nodes) == 0) return(NULL)
start_node <- sample(available_nodes, 1)
bfs_result <- bfs(graph, root = start_node, unreachable = FALSE, order = TRUE, rank = TRUE, father = TRUE)
bfs_order <- intersect(bfs_result$order, available_nodes)
n_subgraph_nodes <- min(sample(min_nodes:max_nodes, 1), length(bfs_order))
subgraph_nodes <- bfs_order[1:n_subgraph_nodes]
return(subgraph_nodes)
}
create_5_subgraphs <- function(graph) {
available_nodes <- V(graph)
subgraphs <- list()
for (i in 1:5) {
subgraph_nodes <- get_connected_subgraph(graph, available_nodes)
if (is.null(subgraph_nodes)) break
subgraphs[[i]] <- subgraph_nodes
available_nodes <- setdiff(available_nodes, subgraph_nodes)
}
return(subgraphs)
}
set.seed(42)
subgraphs <- create_5_subgraphs(g)
subgraph_colors <- c("red", "blue", "green", "yellow", "purple")
node_subgraph_colors <- rep("lightgray", vcount(g))
for (i in 1:length(subgraphs)) {
node_subgraph_colors[subgraphs[[i]]] <- subgraph_colors[i]
}
edge_subgraph_colors <- rep("lightgray", ecount(g))
for (i in 1:length(subgraphs)) {
subgraph_edges <- E(g)[.inc(subgraphs[[i]])]
edge_subgraph_colors[subgraph_edges] <- subgraph_colors[i]
}
plot(g,
layout = layout,
vertex.color = node_subgraph_colors,
vertex.label = node_labels,
vertex.label.color = "black",
vertex.size = 15,
edge.color = edge_subgraph_colors,
edge.width = 2,
main = "Network with 5 Separate Connected Subgraphs")
O resultado acima parece quase correto, mas os nós amarelos (por exemplo, 29) parecem estar violando a conectividade.
Alguma dica sobre como consertar isso?
Escrevi um código opcional para comparar o antes/depois:
node_info <- data.frame(
Node_Index = 1:vcount(g),
Original_Color = node_colors,
New_Color = node_subgraph_colors
)
get_subgraph_number <- function(node) {
subgraph_num <- which(sapply(subgraphs, function(x) node %in% x))
if (length(subgraph_num) == 0) return(NA)
return(subgraph_num)
}
node_info$Subgraph_Number <- sapply(node_info$Node_Index, get_subgraph_number)
head(node_info)
Para complementar a resposta incrível de jblood94, aqui está uma função de plotagem rápida que funciona com a resposta de jblood94:
library(igraph)
library(data.table)
f <- function(g, n) {
m <- length(g)
dt <- setDT(as_data_frame(g))
dt <- rbindlist(list(dt, dt[,.(from = to, to = from)]))
dt[,group := 0L]
used <- logical(m)
s <- sample(m, n)
used[s] <- TRUE
m <- m - n
dt[from %in% s, group := .GRP, from]
while (m) {
dt2 <- unique(
dt[group != 0L & !used[to], .(grow = to, onto = group)][sample(.N)],
by = "grow"
)
dt[dt2, on = .(from = grow), group := onto]
used[dt2[[1]]] <- TRUE
m <- m - nrow(dt2)
}
unique(dt[,to := NULL])[,.(vertices = .(from)), group]
}
plot_multiple_subgraphs <- function(n_plots = 25, n_rows = 10, n_cols = 5, n_subgraphs = 5) {
g <- make_lattice(dimvector = c(n_cols, n_rows))
layout <- layout_on_grid(g, width = n_cols)
n_nodes <- vcount(g)
color_palette <- c("red", "blue", "green", "yellow", "purple")
par(mfrow = c(5, 5), mar = c(0.5, 0.5, 2, 0.5))
for (i in 1:n_plots) {
subgraphs <- f(g, n_subgraphs)
node_colors <- rep("white", n_nodes)
for (j in 1:nrow(subgraphs)) {
nodes <- unlist(subgraphs$vertices[j])
node_colors[nodes] <- color_palette[j]
}
plot(g,
layout = layout,
vertex.color = node_colors,
vertex.label = NA,
vertex.size = 15,
edge.color = "gray",
edge.width = 0.5,
main = paste("Partition", i),
cex.main = 0.8)
}
}
plot_multiple_subgraphs()
Aqui está uma função que seleciona aleatoriamente
n
vértices do gráficog
como o membro inicial do subgráfico para cada um dosn
grupos e, em seguida, "aumenta" iterativamente cada grupo até que todos os vértices estejam em um subgráfico.Demonstrando no gráfico do OP:
Nota: durante as iterações, se vários grupos tentarem "crescer para dentro" do mesmo vértice, o grupo vencedor é selecionado aleatoriamente. Isso é feito com
[sample(.N)]
depois que todos os crescimentos candidatos são encontrados comdt[group != 0L & !used[to], .(grow = to, onto = group)]
.Verificação de desempenho
Testando o desempenho ao particionar uma grade de 100 por 100 em 10 grupos:
Com
igraph::voronoi_cells(g, ...)$membership
:Criado em 2024-09-13 com reprex v2.1.1
Eu diria que a sua
bfs
é uma boa abordagem para começar, e você pode usarbfs
como abaixoonde o tamanho de cada "mini gráfico" conectado é aleatório.
visualização
mostra algo como abaixo por exemplo