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(igraphdata)
## Warning: package 'igraphdata' was built under R version 4.4.3
library(knitr)
data("karate")
karate <- upgrade_graph(karate)
edges <- as_data_frame(karate)
head(edges)
## from to weight
## 1 Mr Hi Actor 2 4
## 2 Mr Hi Actor 3 5
## 3 Mr Hi Actor 4 3
## 4 Mr Hi Actor 5 3
## 5 Mr Hi Actor 6 3
## 6 Mr Hi Actor 7 3
summary(edges)
## from to weight
## Length:78 Length:78 Min. :1.000
## Class :character Class :character 1st Qu.:2.000
## Mode :character Mode :character Median :3.000
## Mean :2.962
## 3rd Qu.:4.000
## Max. :7.000
summary(edges$weight)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.000 2.000 3.000 2.962 4.000 7.000
edges
## from to weight
## 1 Mr Hi Actor 2 4
## 2 Mr Hi Actor 3 5
## 3 Mr Hi Actor 4 3
## 4 Mr Hi Actor 5 3
## 5 Mr Hi Actor 6 3
## 6 Mr Hi Actor 7 3
## 7 Mr Hi Actor 8 2
## 8 Mr Hi Actor 9 2
## 9 Mr Hi Actor 11 2
## 10 Mr Hi Actor 12 3
## 11 Mr Hi Actor 13 1
## 12 Mr Hi Actor 14 3
## 13 Mr Hi Actor 18 2
## 14 Mr Hi Actor 20 2
## 15 Mr Hi Actor 22 2
## 16 Mr Hi Actor 32 2
## 17 Actor 2 Actor 3 6
## 18 Actor 2 Actor 4 3
## 19 Actor 2 Actor 8 4
## 20 Actor 2 Actor 14 5
## 21 Actor 2 Actor 18 1
## 22 Actor 2 Actor 20 2
## 23 Actor 2 Actor 22 2
## 24 Actor 2 Actor 31 2
## 25 Actor 3 Actor 4 3
## 26 Actor 3 Actor 8 4
## 27 Actor 3 Actor 9 5
## 28 Actor 3 Actor 10 1
## 29 Actor 3 Actor 14 3
## 30 Actor 3 Actor 28 2
## 31 Actor 3 Actor 29 2
## 32 Actor 3 Actor 33 2
## 33 Actor 4 Actor 8 3
## 34 Actor 4 Actor 13 3
## 35 Actor 4 Actor 14 3
## 36 Actor 5 Actor 7 2
## 37 Actor 5 Actor 11 3
## 38 Actor 6 Actor 7 5
## 39 Actor 6 Actor 11 3
## 40 Actor 6 Actor 17 3
## 41 Actor 7 Actor 17 3
## 42 Actor 9 Actor 31 3
## 43 Actor 9 Actor 33 3
## 44 Actor 9 John A 4
## 45 Actor 10 John A 2
## 46 Actor 14 John A 3
## 47 Actor 15 Actor 33 3
## 48 Actor 15 John A 2
## 49 Actor 16 Actor 33 3
## 50 Actor 16 John A 4
## 51 Actor 19 Actor 33 1
## 52 Actor 19 John A 2
## 53 Actor 20 John A 1
## 54 Actor 21 Actor 33 3
## 55 Actor 21 John A 1
## 56 Actor 23 Actor 33 2
## 57 Actor 23 John A 3
## 58 Actor 24 Actor 26 5
## 59 Actor 24 Actor 28 4
## 60 Actor 24 Actor 30 3
## 61 Actor 24 Actor 33 5
## 62 Actor 24 John A 4
## 63 Actor 25 Actor 26 2
## 64 Actor 25 Actor 28 3
## 65 Actor 25 Actor 32 2
## 66 Actor 26 Actor 32 7
## 67 Actor 27 Actor 30 4
## 68 Actor 27 John A 2
## 69 Actor 28 John A 4
## 70 Actor 29 Actor 32 2
## 71 Actor 29 John A 2
## 72 Actor 30 Actor 33 4
## 73 Actor 30 John A 2
## 74 Actor 31 Actor 33 3
## 75 Actor 31 John A 3
## 76 Actor 32 Actor 33 4
## 77 Actor 32 John A 4
## 78 Actor 33 John A 5
###### Pake fungi igraph #######
set.seed(6)
lv <- cluster_louvain(karate, weights = edges$weight, resolution = 1)
mem_igraph <- lv$memberships[nrow(lv$memberships), ]
mem <- lv$memberships
modularity(karate, mem[1, ], weights = E(karate)$weight)
## [1] 0.3993366
fase1 <- data.frame(node = V(karate)$name, komunitas = mem[1, ])
fase_1 <- data.frame(komunitas = sort(unique(mem[1, ])),
jumlah = as.integer(table(mem[1, ])))
fase_1
## komunitas jumlah
## 1 1 11
## 2 2 2
## 3 3 3
## 4 4 10
## 5 5 2
## 6 6 4
## 7 7 2
fase_2 <- data.frame(komunitas = sort(unique(mem[2, ])),
jumlah = as.integer(table(mem[2, ])))
fase_2
## komunitas jumlah
## 1 1 11
## 2 2 5
## 3 3 12
## 4 4 6
modularity(karate, mem[2, ], weights = E(karate)$weight)
## [1] 0.4449036
lv$memberships
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
## [1,] 1 1 1 1 2 3 3 1 4 4 2 1 1 1
## [2,] 1 1 1 1 2 2 2 1 3 3 2 1 1 1
## [,15] [,16] [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24] [,25] [,26]
## [1,] 4 4 3 1 4 1 4 1 4 5 6 6
## [2,] 3 3 2 1 3 1 3 1 3 4 4 4
## [,27] [,28] [,29] [,30] [,31] [,32] [,33] [,34]
## [1,] 7 5 6 7 4 6 4 4
## [2,] 3 4 4 3 3 4 3 3
lv$modularity
## [1] 0.3993366 0.4449036
###### Plot #######
set.seed(6)
plot(lv, karate, vertex.size = 8, vertex.label.cex = 0.6,
main = "Clustering dengan Algoritma Louvain")

###### Ekperimen mengganti parameter #######
resolusi <- c(0.19, 0.20, 0.5, 0.75, 1, 1.25, 1.5)
hasil_eksperimen <- data.frame()
for (r in resolusi) {
set.seed(6)
lv <- cluster_louvain(karate, weights = E(karate)$weight, resolution = r)
mem <- lv$memberships[nrow(lv$memberships), ]
Q <- modularity(karate, mem, weights = E(karate)$weight, resolution = r)
hasil_eksperimen <- rbind(hasil_eksperimen,
data.frame(Resolution = r,
Jumlah_Klaster = length(unique(mem)),
Modularitas = Q))
}
hasil_eksperimen
## Resolution Jumlah_Klaster Modularitas
## 1 0.19 1 0.8100000
## 2 0.20 2 0.8045351
## 3 0.50 2 0.6541950
## 4 0.75 2 0.5289116
## 5 1.00 4 0.4449036
## 6 1.25 4 0.3699823
## 7 1.50 4 0.2951875
resolusi_buat_plot <- c(0.19, 0.20, 0.75, 1.5)
par(mfrow = c(2, 2))
for (r in resolusi_buat_plot) {
set.seed(6)
lv <- cluster_louvain(karate, weights = E(karate)$weight, resolution = r)
plot(lv, karate, vertex.size = 8, vertex.label.cex = 0.6,
main = paste("Louvain - Resolusi", r))
}

############################### MANUAL R #####################################
A <- as.matrix(as_adjacency_matrix(karate, attr = "weight", sparse = FALSE))
dim(A)
## [1] 34 34
A[1:5, 1:5]
## Mr Hi Actor 2 Actor 3 Actor 4 Actor 5
## Mr Hi 0 4 5 3 3
## Actor 2 4 0 6 3 0
## Actor 3 5 6 0 3 0
## Actor 4 3 3 3 0 0
## Actor 5 3 0 0 0 0
# Fungsi menghitung modularitas
modularity_manual <- function(A, community, resolution = 1) {
# Total bobot seluruh edge (2m)
two_m <- sum(A)
# Weighted degree setiap vertex
degree <- rowSums(A)
# Mengambil komunitas
communities <- unique(community)
# Inisialisasi modularitas
Q <- 0
for (c in communities) {
# Mengambil indeks vertex dalam komunitas
idx <- which(community == c)
# Menghitung bobot edge internal
internal <- sum(A[idx, idx, drop = FALSE])
# Menghitung total weighted degree komunitas
total_degree <- sum(degree[idx])
# Menghitung modularitas
Q <- Q + (internal / two_m) - resolution * (total_degree / two_m)^2
}
return(Q)
}
# STEP 1: LOCAL MOVING
local_moving <- function(A, resolution = 1, max_iter = 100, tol = 1e-12) {
n <- nrow(A)
# Setiap vertex menjadi komunitas tersendiri
community <- seq_len(n)
# Menghitung weighted degree
degree <- rowSums(A)
# Total bobot edge
m <- sum(A) / 2
# Iterasi
for (iter in seq_len(max_iter)) {
berubah <- FALSE
# Mengacak urutan vertex
urutan <- sample.int(n)
for (i in urutan) {
# Komunitas asal
asal <- community[i]
# Mencari tetangga vertex
tetangga <- which(A[i, ] > 0)
tetangga <- setdiff(tetangga, i)
# Kandidat komunitas tetangga
kandidat <- unique(community[tetangga])
# Menghitung weighted degree komunitas asal
idx_asal <- which(community == asal)
d_asal <- sum(degree[idx_asal])
# Bobot hubungan vertex dengan komunitas asal
k_asal <- sum(A[i, setdiff(idx_asal, i)])
# Inisialisasi perpindahan terbaik
gain_terbaik <- 0
komunitas_terbaik <- asal
# Menguji setiap komunitas tetangga
for (c in setdiff(kandidat, asal)) {
# Indeks anggota komunitas tujuan
idx_tujuan <- which(community == c)
# Weighted degree komunitas tujuan
d_tujuan <- sum(degree[idx_tujuan])
# Bobot hubungan vertex dengan komunitas tujuan
k_tujuan <- sum(A[i, idx_tujuan])
# Menghitung perubahan modularitas
delta_Q <- (k_tujuan - k_asal) / m - resolution * degree[i] *
(d_tujuan - d_asal + degree[i]) / (2 * m^2)
# Memilih perpindahan dengan gain terbesar
if (delta_Q > gain_terbaik + tol) {
gain_terbaik <- delta_Q
komunitas_terbaik <- c
}
}
# Memperbarui komunitas
if (komunitas_terbaik != asal) {
community[i] <- komunitas_terbaik
berubah <- TRUE
}
}
# Menyederhanakan nomor komunitas
community <- match(community, unique(community))
# Berhenti jika tidak ada perubahan
if (!berubah) {
break
}
}
return(community)
}
# Menjalankan Step 1
set.seed(2)
komunitas_step1 <- local_moving(A)
# Melihat hasil komunitas
table(komunitas_step1)
## komunitas_step1
## 1 2 3 4 5 6 7 8
## 5 6 2 3 10 2 4 2
# Menghitung modularitas
modularity_manual(A, komunitas_step1)
## [1] 0.3606567
# STEP 2: COMMUNITY AGGREGATION
aggregate_community <- function(A, community) {
# Mengambil komunitas yang terbentuk
communities <- sort(unique(community))
# Jumlah komunitas
k <- length(communities)
# Membuat matriks adjacency baru
A_new <- matrix(0, nrow = k, ncol = k)
# Menggabungkan hubungan antarvertex
for (i in seq_len(nrow(A))) {
for (j in seq_len(ncol(A))) {
ci <- community[i]
cj <- community[j]
A_new[ci, cj] <- A_new[ci, cj] + A[i, j]
}
}
# Memberikan nama supernode
rownames(A_new) <- paste0("C", seq_len(k))
colnames(A_new) <- paste0("C", seq_len(k))
return(A_new)
}
# Menggabungkan komunitas hasil Step 1
A_step2 <- aggregate_community(A, komunitas_step1)
A_step2
## C1 C2 C3 C4 C5 C6 C7 C8
## C1 18 23 5 6 3 0 2 0
## C2 23 74 0 0 13 2 2 0
## C3 5 0 6 5 0 0 0 0
## C4 6 0 5 22 0 0 0 0
## C5 3 13 0 0 94 13 10 8
## C6 0 2 0 0 13 8 8 3
## C7 2 2 0 0 10 8 26 0
## C8 0 0 0 0 8 3 0 8
dim(A_step2)
## [1] 8 8
# Fungsi utama Louvain manual
louvain_manual <- function(A, resolution = 1, max_iter = 100, max_levels = 20) {
A_original <- A
n_original <- nrow(A)
# Menyimpan anggota asli setiap vertex
members <- as.list(seq_len(n_original))
# Menyimpan hasil setiap level
memberships <- list()
modularities <- numeric()
# Proses Louvain bertingkat
for (level in seq_len(max_levels)) {
# STEP 1: Local Moving
community <- local_moving(A, resolution, max_iter)
# Jumlah komunitas
k <- length(unique(community))
# Menyimpan anggota asli setiap komunitas
new_members <- lapply(seq_len(k), function(c) {
unlist(members[which(community == c)],use.names = FALSE)
}
)
# Mengembalikan keanggotaan ke vertex awal
mem_original <- integer(n_original)
for (c in seq_len(k)) {
mem_original[new_members[[c]]] <- c
}
memberships[[level]] <- mem_original
# Menghitung modularitas pada graf awal
modularities[level] <- modularity_manual(A_original, mem_original, resolution)
# Berhenti jika tidak ada penggabungan
if (k == nrow(A)) {
break
}
# STEP 2: Community Aggregation
A <- aggregate_community(A, community)
# Memperbarui anggota supernode
members <- new_members
}
# Menggabungkan membership setiap level
membership_matrix <- do.call(rbind, memberships)
rownames(membership_matrix) <- paste0("Level_", seq_along(memberships))
colnames(membership_matrix) <- rownames(A_original)
return(list(
memberships = membership_matrix,
modularity = modularities,
levels = length(memberships)
))
}
set.seed(2)
hasil_manual <- louvain_manual(A, resolution = 1)
hasil_manual$memberships
## Mr Hi Actor 2 Actor 3 Actor 4 Actor 5 Actor 6 Actor 7 Actor 8 Actor 9
## Level_1 1 2 2 2 3 4 4 2 5
## Level_2 1 1 1 1 2 2 2 1 3
## Level_3 1 1 1 1 2 2 2 1 3
## Actor 10 Actor 11 Actor 12 Actor 13 Actor 14 Actor 15 Actor 16 Actor 17
## Level_1 5 3 1 2 2 5 5 4
## Level_2 3 2 1 1 1 3 3 2
## Level_3 3 2 1 1 1 3 3 2
## Actor 18 Actor 19 Actor 20 Actor 21 Actor 22 Actor 23 Actor 24 Actor 25
## Level_1 1 5 1 5 1 5 6 7
## Level_2 1 3 1 3 1 3 4 4
## Level_3 1 3 1 3 1 3 4 4
## Actor 26 Actor 27 Actor 28 Actor 29 Actor 30 Actor 31 Actor 32 Actor 33
## Level_1 7 8 6 7 8 5 7 5
## Level_2 4 3 4 4 3 3 4 3
## Level_3 4 3 4 4 3 3 4 3
## John A
## Level_1 5
## Level_2 3
## Level_3 3
hasil_manual$modularity
## [1] 0.3606567 0.4449036 0.4449036
hasil_manual$levels
## [1] 3
mem <- hasil_manual$memberships
mem_manual <- hasil_manual$memberships[nrow(hasil_manual$memberships), ]
for (i in seq_len(nrow(mem))) {
cat("\nLEVEL", i, "\n")
hasil <- data.frame(
Node = V(karate)$name,
Komunitas = mem[i, ]
)
print(kable(hasil))
# Jumlah anggota setiap komunitas
ringkasan <- data.frame(
Komunitas = sort(unique(mem[i, ])),
Jumlah = as.integer(table(mem[i, ]))
)
print(kable(ringkasan))
# Modularitas
cat(
"Modularitas:",
hasil_manual$modularity[i],
"\n"
)
}
##
## LEVEL 1
##
##
## | |Node | Komunitas|
## |:--------|:--------|---------:|
## |Mr Hi |Mr Hi | 1|
## |Actor 2 |Actor 2 | 2|
## |Actor 3 |Actor 3 | 2|
## |Actor 4 |Actor 4 | 2|
## |Actor 5 |Actor 5 | 3|
## |Actor 6 |Actor 6 | 4|
## |Actor 7 |Actor 7 | 4|
## |Actor 8 |Actor 8 | 2|
## |Actor 9 |Actor 9 | 5|
## |Actor 10 |Actor 10 | 5|
## |Actor 11 |Actor 11 | 3|
## |Actor 12 |Actor 12 | 1|
## |Actor 13 |Actor 13 | 2|
## |Actor 14 |Actor 14 | 2|
## |Actor 15 |Actor 15 | 5|
## |Actor 16 |Actor 16 | 5|
## |Actor 17 |Actor 17 | 4|
## |Actor 18 |Actor 18 | 1|
## |Actor 19 |Actor 19 | 5|
## |Actor 20 |Actor 20 | 1|
## |Actor 21 |Actor 21 | 5|
## |Actor 22 |Actor 22 | 1|
## |Actor 23 |Actor 23 | 5|
## |Actor 24 |Actor 24 | 6|
## |Actor 25 |Actor 25 | 7|
## |Actor 26 |Actor 26 | 7|
## |Actor 27 |Actor 27 | 8|
## |Actor 28 |Actor 28 | 6|
## |Actor 29 |Actor 29 | 7|
## |Actor 30 |Actor 30 | 8|
## |Actor 31 |Actor 31 | 5|
## |Actor 32 |Actor 32 | 7|
## |Actor 33 |Actor 33 | 5|
## |John A |John A | 5|
##
##
## | Komunitas| Jumlah|
## |---------:|------:|
## | 1| 5|
## | 2| 6|
## | 3| 2|
## | 4| 3|
## | 5| 10|
## | 6| 2|
## | 7| 4|
## | 8| 2|
## Modularitas: 0.3606567
##
## LEVEL 2
##
##
## | |Node | Komunitas|
## |:--------|:--------|---------:|
## |Mr Hi |Mr Hi | 1|
## |Actor 2 |Actor 2 | 1|
## |Actor 3 |Actor 3 | 1|
## |Actor 4 |Actor 4 | 1|
## |Actor 5 |Actor 5 | 2|
## |Actor 6 |Actor 6 | 2|
## |Actor 7 |Actor 7 | 2|
## |Actor 8 |Actor 8 | 1|
## |Actor 9 |Actor 9 | 3|
## |Actor 10 |Actor 10 | 3|
## |Actor 11 |Actor 11 | 2|
## |Actor 12 |Actor 12 | 1|
## |Actor 13 |Actor 13 | 1|
## |Actor 14 |Actor 14 | 1|
## |Actor 15 |Actor 15 | 3|
## |Actor 16 |Actor 16 | 3|
## |Actor 17 |Actor 17 | 2|
## |Actor 18 |Actor 18 | 1|
## |Actor 19 |Actor 19 | 3|
## |Actor 20 |Actor 20 | 1|
## |Actor 21 |Actor 21 | 3|
## |Actor 22 |Actor 22 | 1|
## |Actor 23 |Actor 23 | 3|
## |Actor 24 |Actor 24 | 4|
## |Actor 25 |Actor 25 | 4|
## |Actor 26 |Actor 26 | 4|
## |Actor 27 |Actor 27 | 3|
## |Actor 28 |Actor 28 | 4|
## |Actor 29 |Actor 29 | 4|
## |Actor 30 |Actor 30 | 3|
## |Actor 31 |Actor 31 | 3|
## |Actor 32 |Actor 32 | 4|
## |Actor 33 |Actor 33 | 3|
## |John A |John A | 3|
##
##
## | Komunitas| Jumlah|
## |---------:|------:|
## | 1| 11|
## | 2| 5|
## | 3| 12|
## | 4| 6|
## Modularitas: 0.4449036
##
## LEVEL 3
##
##
## | |Node | Komunitas|
## |:--------|:--------|---------:|
## |Mr Hi |Mr Hi | 1|
## |Actor 2 |Actor 2 | 1|
## |Actor 3 |Actor 3 | 1|
## |Actor 4 |Actor 4 | 1|
## |Actor 5 |Actor 5 | 2|
## |Actor 6 |Actor 6 | 2|
## |Actor 7 |Actor 7 | 2|
## |Actor 8 |Actor 8 | 1|
## |Actor 9 |Actor 9 | 3|
## |Actor 10 |Actor 10 | 3|
## |Actor 11 |Actor 11 | 2|
## |Actor 12 |Actor 12 | 1|
## |Actor 13 |Actor 13 | 1|
## |Actor 14 |Actor 14 | 1|
## |Actor 15 |Actor 15 | 3|
## |Actor 16 |Actor 16 | 3|
## |Actor 17 |Actor 17 | 2|
## |Actor 18 |Actor 18 | 1|
## |Actor 19 |Actor 19 | 3|
## |Actor 20 |Actor 20 | 1|
## |Actor 21 |Actor 21 | 3|
## |Actor 22 |Actor 22 | 1|
## |Actor 23 |Actor 23 | 3|
## |Actor 24 |Actor 24 | 4|
## |Actor 25 |Actor 25 | 4|
## |Actor 26 |Actor 26 | 4|
## |Actor 27 |Actor 27 | 3|
## |Actor 28 |Actor 28 | 4|
## |Actor 29 |Actor 29 | 4|
## |Actor 30 |Actor 30 | 3|
## |Actor 31 |Actor 31 | 3|
## |Actor 32 |Actor 32 | 4|
## |Actor 33 |Actor 33 | 3|
## |John A |John A | 3|
##
##
## | Komunitas| Jumlah|
## |---------:|------:|
## | 1| 11|
## | 2| 5|
## | 3| 12|
## | 4| 6|
## Modularitas: 0.4449036
# Membandingkan hasil
perbandingan <- data.frame(
Metode = c("Louvain igraph", "Louvain Manual"),
Jumlah_Klaster = c(
length(unique(mem_igraph)),
length(unique(mem_manual))
),
Modularitas = c(
modularity(
karate,
mem_igraph,
weights = E(karate)$weight
),
hasil_manual$modularity[
length(hasil_manual$modularity)
]
)
)
kable(perbandingan)
| Louvain igraph |
4 |
0.4449036 |
| Louvain Manual |
4 |
0.4449036 |
par(mfrow = c(1, 1))
set.seed(2)
komunitas_manual <- make_clusters(membership = mem_manual, graph = karate)
plot(komunitas_manual, karate, vertex.size = 8, vertex.label.cex = 0.6,
main = "Clustering dengan Algoritma Louvain Manual")
