Ishodi učenja:
Razlikovati strukturnu i regularnu ekvivalenciju te uloge u mreži.
Primijeniti pozicijsku analizu (značajke + grupiranje/blokovi).
Analizirati hijerarhiju i objasniti uloge (gatekeeper, broker, autoritet).
Kritički raspraviti dualnost čvor–veza i što se gubi/uzima modeliranjem.
library(igraph)
library(tidygraph)
library(ggraph)
library(sna)
Mjere centralnosti odgovaraju na pitanje tko je važan u mreži, ali ne i na pitanje koju funkciju čvor obavlja niti u koju strukturnu poziciju pripada. U teoriji mreža i primjenama SNA, “važnost” je uvijek uvjetovana fenomenom koji graf modelira: isti stupanj, blizina ili posredništvo mogu značiti utjecaj u društvenoj mreži, ali opterećenje ili kapacitet u komunikacijskoj mreži; stoga se centralnosti moraju interpretirati “s oprezom”, jer jedna mjera ne mora detektirati ono što je relevantno za konkretan proces (npr. popularnost ne mora biti isto što i sposobnost širenja informacija). Na primjer, recimo da se promatra jedna mreža učenika u razredu - čvor s najvišom blizinom (closeness) nije nužno ključan za protok informacija: uklanjanje takvog čvora može imati mali učinak, dok čvorovi s visokim posredništvom (betweenness) mogu biti presudni za spajanje podskupina i održavanje povezanosti. To jasno sugerira da centralnosti prvenstveno mjere individualnu poziciju u prostoru putova, ali slabije zahvaćaju mezorazinske obrasce (npr. granice grupa, strukturne rupe) i makro-arhitekturu (npr. jezgra–periferija, hijerarhija).
Osim toga, centralnost ne zahvaća koncept “zamjenjivosti” aktera. Dva čvora mogu imati slične vrijednosti stupnja ili centralnosti, a ipak pripadati različitim strukturnim ulogama; obrnuto, akteri koji obavljaju istu ulogu mogu biti povezani s različitim osobama. Zato se uvodi ideja ekvivalencije i pozicija: strukturna ekvivalencija zahtijeva identične susjede (što je u empirijskim mrežama rijetko), pa se u praksi prelazi na mjerenje strukturne sličnosti preko udaljenosti između redaka/stupaca matrice susjedstva. Upravo taj korak označava prijelaz s “važnosti pojedinca” na “pozicije u sustavu”, što je temelj za uloge poput brokera, gatekeepera ili autoriteta/huba: centralnost može sugerirati tko je istaknut, ali tek pozicijska analiza (ekvivalencije, blokmodeli) objašnjava zašto i u kojoj strukturi je taj istaknuti položaj nastao.
U usmjerenim i ponderiranim mrežama važno je razlikovati “centralnost” od “prestiža” (i općenito od asimetričnih relacija): centralnost tipično polazi od udaljenosti “od čvora prema drugima”, dok prestiž prirodno naglašava tok “prema čvoru” (npr. indegree kao stupanj prestiža, odnosno influence domain kao širi doseg utjecaja prema čvoru). To je važan uvod u hijerarhijske interpretacije i uloge u usmjerenim sustavima (npr. autoriteti i hubovi), gdje ista vrijednost klasične centralnosti ne mora razlikovati “dobar izvor” od “dobrog posrednika”. U tom smislu, centralnosti su nužan početni alat, ali nisu dovoljne za analizu uloga: za uloge su potrebni koncepti ekvivalencije, blokmodeliranja i hijerarhije, jer oni pomiču fokus s individualnog rangiranja na strukturne klase i odnose među klasama.
Osim pripreme, ovdje će se prikazati uvidi u podatke kakve bismo stekli kroz ranije naučene korake u analizi. Za potrebe prikaza, koristit će se dvije mreže.
Prva je Enron mreža, koja nam je osobito prikladna za ovu analizu jer sadrži podatke o poslovnoj funkciji. Drugu mrežu ćemo kreirati temeljem web scrape-a stranica Unipu kako bismo kreirali mrežu izvođača na kolegijima.
Enron e-mail korpus nastao je kao posljedica regulatorne i sudske obrade slučaja Enron: velik dio interne e-mail komunikacije zaposlenika postao je javno dostupan u sklopu istraga i kasnijih objava, pa je dataset široko korišten u istraživanjima organizacijske komunikacije, forenzike, detekcije obrazaca (npr. zajednice, posrednici) te kao standardni “benchmark” u analizi društvenih mreža i rudarstvu teksta.
U ovom prikazu Enron se promatra kao usmjerena mreža komunikacije (pošiljatelj → primatelj), što je prirodno za modele gdje relacija nije nužno simetrična (tko šalje i tko prima). U usmjerenim grafovima zato tipično razlikujemo in/out susjedstva i oslanjamo se na slabo i jako povezane komponente pri analizi povezanosti.
U nastavku je uvodna provjera i osnovna topološka analiza Enron e-mail mreže. U prvom koraku provjeravaju se osnovna svojstva grafa, dostupni atributi čvorova i bridova te osnovni pokazatelji (broj čvorova, bridova, petlje, multi-bridovi). Povezanost i komponente u usmjerenim mrežama tipično se promatraju kroz slabo povezane (weak) i jako povezane (strong) komponente.
library(igraphdata)
data("enron")
g_enron <- igraph::upgrade_graph(enron)
vcount(g_enron)
## [1] 184
ecount(g_enron)
## [1] 125409
igraph::is_directed(g_enron)
## [1] TRUE
any_loop(g_enron)
## [1] TRUE
any_multiple(g_enron)
## [1] TRUE
list(
vertex_attributes = vertex_attr_names(g_enron),
edge_attributes = edge_attr_names(g_enron)
)
## $vertex_attributes
## [1] "Email" "Name" "Note"
##
## $edge_attributes
## [1] "Time" "Reciptype" "Topic" "LDC_topic"
unique(V(g_enron)$Note)
## [1] "Employee, Specialist"
## [2] "Vice President"
## [3] "NA"
## [4] "Director"
## [5] "Vice President, Enron Online"
## [6] "President, Enron Global Mkts"
## [7] "Employee, Associate"
## [8] "Employee"
## [9] "Vice President, Enron WholeSale Services"
## [10] "Employee, Senior Analyst Cash"
## [11] "Manager"
## [12] "Trader"
## [13] "Vice President, Term"
## [14] "Manager, Logistics Manager"
## [15] "CEO, Enron North America and Enron Enery Services"
## [16] "Vice President, Government Affairs"
## [17] "Managing Director, Legal Department"
## [18] "Employee, Senior Specialist"
## [19] "Employee, Analyst"
## [20] "President"
## [21] "Employee, Cash Analyst"
## [22] "In House Lawyer"
## [23] "Employee, Government Relation Executive"
## [24] "Managing Director"
## [25] "CEO"
## [26] "Manager, Risk Management Head"
## [27] "Vice President, Vice President & Chief of Staff"
## [28] "Vice President, Enery marketting and trading Florida"
## [29] "Manager, Real time Trading Desk"
## [30] "CEO, Enron America"
## [31] "Employee, Senior Specialist Logistics"
## [32] "Director, Pipeline Business"
## [33] "President, Enron Online"
## [34] "Employee, Administrative Asisstant"
## [35] "Employee, Analyst Risk Management"
## [36] "Employee, Sr.Specialist"
## [37] "Vice President, Regulatory Affairs"
## [38] "Manager, Chief Risk Management Officer"
## [39] "Vice President, Also Chief Financial Officer and Treasurer"
## [40] "Employee, Chief Operating Officer"
## [41] "President, Enron Gas Pipeline"
## [42] "Manager, Regulatory Affairs"
g_enron <- igraph::simplify(
g_enron,
remove.loops = TRUE, # po želji (možete staviti FALSE)
remove.multiple = TRUE, # spaja multi-bridove
edge.attr.comb = list(
weight = "sum", # broj poruka po paru (i -> j)
Time = toString, # sačuva sve vremenske oznake kao string
Reciptype = toString, # sačuva sve tipove primatelja kao string
Topic = toString, # sačuva sve topic kodove kao string
LDC_topic = toString # sačuva sve LDC_topic kao string
)
)
vcount(g_enron)
## [1] 184
ecount(g_enron)
## [1] 3010
g_enron
## IGRAPH 567507f D--- 184 3010 -- Enron email network
## + attr: LDC_names (g/c), LDC_desc (g/c), name (g/c), Citation (g/c),
## | Email (v/c), Name (v/c), Note (v/c), Time (e/c), Reciptype (e/c),
## | Topic (e/c), LDC_topic (e/c)
## + edges from 567507f:
## [1] 1-> 10 1-> 21 1-> 49 1-> 92 1->105 1->153 2-> 16 2-> 40 2-> 44 2-> 69
## [11] 2-> 71 2-> 76 2-> 83 2-> 89 2->108 2->158 2->160 3-> 11 3-> 18 3-> 44
## [21] 3-> 51 3-> 89 3->140 3->145 3->155 3->157 3->158 3->167 4-> 57 4->119
## [31] 4->141 5-> 2 5-> 6 5-> 7 5-> 11 5-> 22 5-> 27 5-> 42 5-> 46 5-> 52
## [41] 5-> 54 5-> 71 5-> 79 5-> 81 5-> 83 5-> 85 5-> 86 5-> 90 5-> 97 5->102
## [51] 5->106 5->108 5->115 5->128 5->154 5->158 5->160 5->170 5->180 5->182
## + ... omitted several edges
Enron e-mail mreža u startu dolazi kao “sirova” mreža poruka: isti
par osoba može imati jako puno poruka, mogu postojati petlje (netko
šalje sam sebi) i višestruki bridovi (više poruka između istog
pošiljatelja i primatelja). Zato smo mrežu pojednostavnili
(simplify) tako da za svaki par (u → v) ostaje
jedan brid, a atribut weight predstavlja ukupan broj poruka poslanih s
u na v. Time prelazimo s logova poruka na pregledniju
mrežu komunikacijskih odnosa.
Kod usmjerenih mreža razlikuju se slabo povezane i jako povezane komponente. U praksi se za mnoge globalne mjere (npr. prosječna duljina puta, dijametar) analizira najveća slabo povezana komponenta (giant weak component), jer su te mjere inače nedefinirane ili trivijalne u nepovezanim grafovima. Dakle, odatle krećemo.
# Slabo povezane komponente (weak)
comp_w <- igraph::components(g_enron, "weak")
comp_w$no
## [1] 3
summary(comp_w$csize)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.00 1.00 1.00 61.33 91.50 182.00
# Najveća slabo povezana komponenta
giant_id <- which.max(comp_w$csize)
V_giant <- V(g_enron)[comp_w$membership == giant_id]
g_giant <- induced_subgraph(g_enron, vids = V_giant)
vcount(g_giant)
## [1] 182
ecount(g_giant)
## [1] 3010
# Jako povezane komponente (strong) – često ih ima mnogo u e-mail mrežama
comp_s <- igraph::components(g_enron, "strong")
comp_s$no
## [1] 11
summary(comp_s$csize)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.00 1.00 1.00 16.73 1.00 174.00
Kod komponenti vidimo 3 slabo povezane komponente, pri čemu najveća (giant weak component) sadrži 182 od 184 čvora. To znači da gotovo svi zaposlenici pripadaju jednoj velikoj komunikacijskoj “cjelini” ako zanemarimo smjer (tj. ako postoji put u bilo kojem smjeru). Preostala dva čvora su izolirani u smislu globalne povezanosti (ili su u vrlo malim, odvojenim dijelovima mreže). S druge strane, imamo 11 jako povezanih komponenti, a najveća ima 174 čvora: to sugerira da unutar najvećeg dijela mreže postoji mnogo uzajamne komunikacije (postoji put u oba smjera između velikog broja parova), ali ipak nisu svi međusobno dosežni po smjeru, što je tipično za e-mail (npr. neki šalju svima, ali ne dobivaju odgovor; ili postoje hijerarhijske komunikacije).
Gustoća opisuje udio ostvarenih veza u odnosu na maksimalno moguće veze za zadani broj čvorova; u velikim komunikacijskim mrežama očekivano je da je mreža rijetka (sparse). Nakon simplifikacije mreža ima 184 čvora (osobe) i 3010 usmjerenih bridova (različitih komunikacijskih parova). U odnosu na maksimalno mogućih 184⋅183=33672 usmjerenih veza (bez petlji), ovih 3010 veza znači da je mreža rijetka (gustoća oko 0.089). To je očekivano za organizacijsku komunikaciju: većina ljudi ne komunicira izravno sa svima, nego unutar manjih radnih krugova, uz nekoliko “povezivača” koji spajaju grupe.
# Gustoća na originalnoj mreži i na najvećoj komponenti
dens_all <- edge_density(g_enron, loops = FALSE)
dens_giant <- edge_density(g_giant, loops = FALSE)
# Osnovne globalne mjere na najvećoj komponenti (geodezije računamo na usmjerenom grafu)
# Napomena: dijametar/prosječna duljina puta mogu se računati i na nedirigiranoj projekciji
diam_dir <- diameter(g_giant, directed = TRUE, unconnected = FALSE)
apl_dir <- mean_distance(g_giant, directed = TRUE, unconnected = FALSE)
diam_und <- diameter(as.undirected(g_giant, mode = "collapse"), directed = FALSE)
apl_und <- mean_distance(as.undirected(g_giant, mode = "collapse"), directed = FALSE)
list(
density_all = dens_all,
density_giant = dens_giant,
diameter_directed = diam_dir,
apl_directed = apl_dir,
diameter_undirected = diam_und,
apl_undirected = apl_und
)
## $density_all
## [1] 0.08939178
##
## $density_giant
## [1] 0.09137272
##
## $diameter_directed
## [1] Inf
##
## $apl_directed
## [1] Inf
##
## $diameter_undirected
## [1] 4
##
## $apl_undirected
## [1] 2.085787
Kod globalnih mjera udaljenosti uočavamo da su usmjereni dijametar i prosječna duljina puta “Inf”. To nije greška: to samo znači da u usmjerenom smislu postoje parovi čvorova između kojih ne postoji usmjereni put (npr. A može doći do B, ali B ne može doći do A; ili uopće nema puta u zadanom smjeru). Zato za dojam koliko je mreža kompaktna često gledamo i neusmjereni prikaz: tamo je dijametar 4, a prosječna duljina puta oko 2.09. Drugim riječima, u prosjeku su zaposlenici udaljeni otprilike “dva koraka” komunikacije, a najudaljeniji su četiri koraka — što upućuje na relativno “mali svijet” u organizacijskom smislu (informacija može brzo proći kroz nekoliko posrednika).
Detekcija zajednica - provest ćemo jednu varijantu s usmjerenom i jednu s neusmjerenom mrežom. Nakon detekcije zajednica, broje se bridovi koji su unutar zajednica i između zajednica, što daje jednostavnu sliku modularne strukture.
gU <- as.undirected(g_giant, mode = "collapse")
set.seed(1)
comm <- cluster_louvain(gU) # brza i često dobra početna opcija
comm
## IGRAPH clustering multi level, groups: 6, mod: 0.34
## + groups:
## $`1`
## [1] 1 10 14 21 31 49 56 79 91 104 118 128 130 141 147 151
##
## $`2`
## [1] 2 3 11 12 15 16 18 23 25 30 37 40 44 46 47 50 51 55
## [19] 57 61 66 69 71 75 78 80 81 82 84 88 92 97 99 101 106 110
## [37] 114 116 126 129 136 138 153 154 155 156 158 159 161 164 165 167 168 171
## [55] 172 174 178 179 181
##
## $`3`
## + ... omitted several groups/vertices
membership <- membership(comm)
V(g_giant)$louvain <- membership(comm)
table(membership) |> sort(decreasing = TRUE) |> head(10)
## membership
## 2 4 3 5 6 1
## 59 36 30 24 17 16
# Bridovi unutar / između zajednica (na nedirigiranoj mreži)
endsU <- ends(gU, E(gU), names = FALSE)
m1 <- membership[endsU[,1]]
m2 <- membership[endsU[,2]]
within_edges <- sum(m1 == m2)
between_edges <- sum(m1 != m2)
list(
n_communities = length(unique(membership)),
within_edges = within_edges,
between_edges = between_edges,
within_share = within_edges / ecount(gU),
between_share = between_edges / ecount(gU),
modularity = modularity(comm)
)
## $n_communities
## [1] 6
##
## $within_edges
## [1] 1194
##
## $between_edges
## [1] 903
##
## $within_share
## [1] 0.5693848
##
## $between_share
## [1] 0.4306152
##
## $modularity
## [1] 0.3407308
Detekcija zajednica na neusmjerenoj projekciji (Louvain) daje 6 zajednica uz modularnost oko 0.34. To je umjerena modularnost: mreža ima prepoznatljive grupe, ali one nisu potpuno odvojene. U istom smjeru govori i udio bridova: oko 57% veza je unutar zajednica, a oko 43% između zajednica. To znači da većina komunikacije ide “unutar svojih” zajednica, ali značajan dio prelazi granice zajednica — što je realno za firmu gdje postoje timovi/odjeli, ali i koordinacija između njih.
set.seed(1)
comm <- cluster_walktrap(g_giant) # brza i često dobra početna opcija
comm
## IGRAPH clustering walktrap, groups: 8, mod: 0.34
## + groups:
## $`1`
## [1] 19 33 36 41 45 65 77 86 100 120 124 137
##
## $`2`
## [1] 2 3 4 7 11 15 16 17 18 20 23 25 27 30 37 40 42 44
## [19] 46 48 50 55 57 60 61 62 63 69 71 75 78 80 81 84 88 89
## [37] 90 93 97 99 101 102 106 115 116 117 118 119 125 126 128 129 131 133
## [55] 134 135 136 138 139 140 142 147 153 155 156 158 163 165 167 170 172 173
## [73] 174 175 178 179 181
##
## + ... omitted several groups/vertices
membership <- membership(comm)
V(g_giant)$walktrap <- membership(comm)
table(membership) |> sort(decreasing = TRUE) |> head(10)
## membership
## 2 3 5 4 1 6 7 8
## 77 60 17 13 12 1 1 1
# Bridovi unutar / između zajednica (na nedirigiranoj mreži)
endsU <- ends(g_giant, E(g_giant), names = FALSE)
m1 <- membership[endsU[,1]]
m2 <- membership[endsU[,2]]
within_edges <- sum(m1 == m2)
between_edges <- sum(m1 != m2)
list(
n_communities = length(unique(membership)),
within_edges = within_edges,
between_edges = between_edges,
within_share = within_edges / ecount(g_giant),
between_share = between_edges / ecount(g_giant),
modularity = modularity(comm)
)
## $n_communities
## [1] 8
##
## $within_edges
## [1] 2134
##
## $between_edges
## [1] 876
##
## $within_share
## [1] 0.7089701
##
## $between_share
## [1] 0.2910299
##
## $modularity
## [1] 0.3378866
Kod Walktrap algoritma dobivamo 8 zajednica uz modularnost 0.338 (\(≈0.34\)), što je vrlo blizu Louvainu: mreža ima jasnu, ali ne ekstremno krutu modularnu strukturu (postoje grupe, ali imaju i dosta međusobnih veza).
Oko 71% komunikacijskih veza (agregiranih parova pošiljatelj→primatelj) ostaje unutar iste zajednice, dok oko 29% ide preko granica zajednica. To je tipičan obrazac za organizaciju: većina komunikacije je “intra-tim / intra-odjel”, ali gotovo trećina odnosa ipak povezuje različite grupe, što upućuje na koordinaciju, hijerarhiju, podršku ili posrednike koji povezuju dijelove firme.
Što se tiče veličina zajednica (membership tablica): jedna zajednica je vrlo velika (77 čvorova), druga također velika (60), a zatim slijede manje (17, 13, 12) i tri “mini” zajednice od po 1 čvor. To obično znači jedno od sljedećeg (ili kombinaciju):
u mreži postoje glavni komunikacijski “blokovi” (npr. veći odjeli ili funkcionalne cjeline),
uz njih postoje specijalizirane ili slabije povezane skupine,
pojedinačni čvorovi mogu biti izolirani u smislu strukture zajednica (npr. komuniciraju vrlo specifično ili imaju veze koje ih ne “uvlače” u klaster).
Isto možemo uočiti kroz detaljniji presjek:
# Matrica broja bridova između zajednica
mix_mat <- matrix(0, nrow = length(unique(membership)), ncol = length(unique(membership))) #walktrap
for(i in seq_len(nrow(endsU))) {
a <- membership[endsU[i,1]]
b <- membership[endsU[i,2]]
mix_mat[a,b] <- mix_mat[a,b] + 1
mix_mat[b,a] <- mix_mat[b,a] + 1
}
diag(mix_mat) <- diag(mix_mat) / 2 # jer smo brojali simetrično
mix_mat[1:min(10,nrow(mix_mat)), 1:min(10,ncol(mix_mat))]
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,] 45 7 74 5 7 0 0 0
## [2,] 7 857 589 19 9 0 0 1
## [3,] 74 589 967 24 139 1 1 0
## [4,] 5 19 24 83 0 0 0 0
## [5,] 7 9 139 0 182 0 0 0
## [6,] 0 0 1 0 0 0 0 0
## [7,] 0 0 1 0 0 0 0 0
## [8,] 0 1 0 0 0 0 0 0
Za komunikacijske mreže smisleno je izračunati barem: in/out-degree, betweenness (posredovanje), closeness, eigenvector / PageRank (utjecaj/prestiž). U praksi je korisno usporediti top-liste prema više mjera jer hvataju različite uloge čvorova.
# Centralnosti računamo na najvećoj komponenti
# (a) stupnjevi (usmjereno)
deg_in <- igraph::degree(g_giant, mode = "in", normalized = TRUE)
deg_out <- igraph::degree(g_giant, mode = "out", normalized = TRUE)
# (b) betweenness
bet <- igraph::betweenness(gU, normalized = TRUE)
# (c) closeness
clo <- igraph::closeness(gU, normalized = TRUE)
# (d) eigenvector i PageRank
eig <- igraph::eigen_centrality(gU, directed = FALSE, scale = TRUE)$vector
pr <- igraph::page_rank(g_giant, directed = TRUE)$vector
cent <- data.frame(
indegree = as.numeric(deg_in),
outdegree = as.numeric(deg_out),
betweenness = as.numeric(bet),
closeness = as.numeric(clo),
eigen = as.numeric(eig),
pagerank = as.numeric(pr)
)
# Top 10 po različitim mjerama
top_n <- function(df, col, n = 10) df[order(df[[col]], decreasing = TRUE), ][1:n, ]
list(
top_indegree = top_n(cent, "indegree", 10),
top_outdegree = top_n(cent, "outdegree", 10),
top_betweenness = top_n(cent, "betweenness", 10),
top_closeness = top_n(cent, "closeness", 10),
top_eigen = top_n(cent, "eigen", 10),
top_pagerank = top_n(cent, "pagerank", 10)
)
## $top_indegree
## indegree outdegree betweenness closeness eigen pagerank
## 82 0.3314917 0.5524862 0.123418305 0.7098039 1.0000000 0.019499337
## 107 0.3149171 0.4033149 0.040072742 0.6284722 0.8414783 0.017867863
## 125 0.2651934 0.2596685 0.017726153 0.5857605 0.5672020 0.016988577
## 156 0.2486188 0.2375691 0.021486735 0.5819936 0.6104714 0.013002370
## 52 0.2320442 0.1270718 0.005054067 0.5535168 0.5727606 0.011731762
## 78 0.1988950 0.2099448 0.011122947 0.5656250 0.5785657 0.011014652
## 28 0.1933702 0.1988950 0.009411082 0.5621118 0.5743921 0.008925100
## 7 0.1878453 0.1657459 0.015235219 0.5603715 0.5091306 0.009453853
## 139 0.1878453 0.1878453 0.012614311 0.5535168 0.4944151 0.009595810
## 5 0.1657459 0.1602210 0.006188785 0.5501520 0.5326117 0.008531714
##
## $top_outdegree
## indegree outdegree betweenness closeness eigen pagerank
## 82 0.3314917 0.5524862 0.12341831 0.7098039 1.0000000 0.019499337
## 152 0.1602210 0.4696133 0.10788023 0.6630037 0.8378317 0.007751664
## 105 0.1049724 0.4640884 0.09578141 0.6557971 0.8460215 0.005367017
## 107 0.3149171 0.4033149 0.04007274 0.6284722 0.8414783 0.017867863
## 94 0.1325967 0.3038674 0.04463799 0.6033333 0.6713991 0.006639500
## 125 0.2651934 0.2596685 0.01772615 0.5857605 0.5672020 0.016988577
## 64 0.1104972 0.2541436 0.02396730 0.5586420 0.4874609 0.006666727
## 156 0.2486188 0.2375691 0.02148674 0.5819936 0.6104714 0.013002370
## 166 0.1602210 0.2320442 0.04816564 0.5586420 0.3899419 0.008328763
## 90 0.1270718 0.2265193 0.01707497 0.5339233 0.3680846 0.007235625
##
## $top_betweenness
## indegree outdegree betweenness closeness eigen pagerank
## 82 0.3314917 0.5524862 0.12341831 0.7098039 1.0000000 0.019499337
## 152 0.1602210 0.4696133 0.10788023 0.6630037 0.8378317 0.007751664
## 105 0.1049724 0.4640884 0.09578141 0.6557971 0.8460215 0.005367017
## 166 0.1602210 0.2320442 0.04816564 0.5586420 0.3899419 0.008328763
## 94 0.1325967 0.3038674 0.04463799 0.6033333 0.6713991 0.006639500
## 107 0.3149171 0.4033149 0.04007274 0.6284722 0.8414783 0.017867863
## 64 0.1104972 0.2541436 0.02396730 0.5586420 0.4874609 0.006666727
## 156 0.2486188 0.2375691 0.02148674 0.5819936 0.6104714 0.013002370
## 125 0.2651934 0.2596685 0.01772615 0.5857605 0.5672020 0.016988577
## 90 0.1270718 0.2265193 0.01707497 0.5339233 0.3680846 0.007235625
##
## $top_closeness
## indegree outdegree betweenness closeness eigen pagerank
## 82 0.3314917 0.5524862 0.123418305 0.7098039 1.0000000 0.019499337
## 152 0.1602210 0.4696133 0.107880232 0.6630037 0.8378317 0.007751664
## 105 0.1049724 0.4640884 0.095781408 0.6557971 0.8460215 0.005367017
## 107 0.3149171 0.4033149 0.040072742 0.6284722 0.8414783 0.017867863
## 94 0.1325967 0.3038674 0.044637994 0.6033333 0.6713991 0.006639500
## 125 0.2651934 0.2596685 0.017726153 0.5857605 0.5672020 0.016988577
## 156 0.2486188 0.2375691 0.021486735 0.5819936 0.6104714 0.013002370
## 78 0.1988950 0.2099448 0.011122947 0.5656250 0.5785657 0.011014652
## 28 0.1933702 0.1988950 0.009411082 0.5621118 0.5743921 0.008925100
## 7 0.1878453 0.1657459 0.015235219 0.5603715 0.5091306 0.009453853
##
## $top_eigen
## indegree outdegree betweenness closeness eigen pagerank
## 82 0.3314917 0.5524862 0.123418305 0.7098039 1.0000000 0.019499337
## 105 0.1049724 0.4640884 0.095781408 0.6557971 0.8460215 0.005367017
## 107 0.3149171 0.4033149 0.040072742 0.6284722 0.8414783 0.017867863
## 152 0.1602210 0.4696133 0.107880232 0.6630037 0.8378317 0.007751664
## 94 0.1325967 0.3038674 0.044637994 0.6033333 0.6713991 0.006639500
## 156 0.2486188 0.2375691 0.021486735 0.5819936 0.6104714 0.013002370
## 78 0.1988950 0.2099448 0.011122947 0.5656250 0.5785657 0.011014652
## 28 0.1933702 0.1988950 0.009411082 0.5621118 0.5743921 0.008925100
## 52 0.2320442 0.1270718 0.005054067 0.5535168 0.5727606 0.011731762
## 125 0.2651934 0.2596685 0.017726153 0.5857605 0.5672020 0.016988577
##
## $top_pagerank
## indegree outdegree betweenness closeness eigen pagerank
## 82 0.3314917 0.55248619 0.123418305 0.7098039 1.0000000 0.019499337
## 107 0.3149171 0.40331492 0.040072742 0.6284722 0.8414783 0.017867863
## 125 0.2651934 0.25966851 0.017726153 0.5857605 0.5672020 0.016988577
## 156 0.2486188 0.23756906 0.021486735 0.5819936 0.6104714 0.013002370
## 96 0.1381215 0.11049724 0.007830343 0.5402985 0.3925672 0.012152026
## 52 0.2320442 0.12707182 0.005054067 0.5535168 0.5727606 0.011731762
## 78 0.1988950 0.20994475 0.011122947 0.5656250 0.5785657 0.011014652
## 132 0.1602210 0.18232044 0.009060741 0.5586420 0.4676761 0.010243417
## 50 0.1546961 0.03867403 0.003816161 0.5142045 0.3319927 0.009890089
## 66 0.1657459 0.09944751 0.005077675 0.5142045 0.3861329 0.009856169
Mjere centralnosti pokazuju da se isti čvor (u našem ispisu to je indeks 82) pojavljuje na vrhu u više mjera (in-degree, out-degree, betweenness, closeness, eigenvector, PageRank). To je tipičan znak hub-a / ključnog posrednika: osoba ima mnogo dolaznih i odlaznih kontakata (stupnjevi), nalazi se na mnogim najkraćim putevima (betweenness), relativno je blizu svima (closeness), povezana je s drugim dobro povezanim čvorovima (eigenvector) i ima visok “prestiž” u usmjerenom smislu (PageRank). Čvorovi koji su visoko po out-degree, ali ne nužno po in-degree, često odgovaraju ulogama “broadcasta” (npr. netko tko šalje obavijesti), dok visoki in-degree može značiti da se osoba često kontaktira (npr. koordinacija, podrška, administracija ili menadžment).
set.seed(3)
lay_enron <- layout_with_fr(g_giant)
par(mfrow=c(3,2))
V(g_giant)$indegree <- cent$indegree
V(g_giant)$outdegree <- cent$outdegree
V(g_giant)$close <- cent$closeness
V(g_giant)$betwe <- cent$betweenness
V(g_giant)$eigen <- cent$eigen
V(g_giant)$pagerank <- cent$pagerank
plot(
g_giant,
layout = lay_enron,
vertex.size = V(g_giant)$indegree*20,
vertex.label = NA,
vertex.color = V(g_giant)$walktrap,
edge.arrow.size = 0.25,
edge.width = E(g_giant)$weight,
main = "Enron mreža (giant weak component) \n veličina čvora = indegree"
)
plot(
g_giant,
layout = lay_enron,
vertex.size = V(g_giant)$outdegree*20,
vertex.label = NA,
vertex.color = V(g_giant)$walktrap,
edge.arrow.size = 0.25,
edge.width = E(g_giant)$weight,
main = "Enron mreža (giant weak component) \n veličina čvora = outdegree"
)
plot(
g_giant,
layout = lay_enron,
vertex.size = V(g_giant)$close*20,
vertex.label = NA,
vertex.color = V(g_giant)$walktrap,
edge.arrow.size = 0.25,
edge.width = E(g_giant)$weight,
main = "Enron mreža (giant weak component) \n veličina čvora = closeness"
)
plot(
g_giant,
layout = lay_enron,
vertex.size = V(g_giant)$betwe*200,
vertex.label = NA,
vertex.color = V(g_giant)$walktrap,
edge.arrow.size = 0.25,
edge.width = E(g_giant)$weight,
main = "Enron mreža (giant weak component) \n veličina čvora = betweenness"
)
plot(
g_giant,
layout = lay_enron,
vertex.size = V(g_giant)$eigen*20,
vertex.label = NA,
vertex.color = V(g_giant)$walktrap,
edge.arrow.size = 0.25,
edge.width = E(g_giant)$weight,
main = "Enron mreža (giant weak component) \n veličina čvora = eigenvector"
)
plot(
g_giant,
layout = lay_enron,
vertex.size = V(g_giant)$pagerank*500,
vertex.label = NA,
vertex.color = V(g_giant)$walktrap,
edge.arrow.size = 0.25,
edge.width = E(g_giant)$weight,
main = "Enron mreža (giant weak component) \n veličina čvora = page rank"
)
Vizualizacije s bojom po zajednici i veličinom čvora po različitim centralnostima pomažu da se ove interpretacije “vide”: velike točke koje su ujedno na granicama boja često su mostovi između zajednica (visok betweenness), dok velike točke duboko unutar jedne boje često označavaju lokalne centre unutar jedne grupe (visok eigenvector/closeness u tom klasteru). U daljnjoj analizi ima smisla povezati top-čvorove s atributom Note (uloga/pozicija) i provjeriti jesu li “centralni” čvorovi doista menadžment/koordinacija ili se radi o operativnim ulogama koje prirodno komuniciraju sa svima (npr. pravna služba, pomoćnici, IT podrška).
Za drugi skup podataka, koristit ćemo suradnju nastavnika na kolegijima na našem Sveučilištu. Ovdje ne promatramo “tko s kim prijateljuje”, nego strukturni trag nastavne organizacije: čvorovi su nastavnici, a bridovi nastaju jer se osobe pojavljuju na istom kolegiju (kao nositelji ili izvođači). Time mreža istodobno hvata suradničku dimenziju (N–N i I–I su simetrične veze) i dimenziju odgovornosti (I→N je usmjereni odnos prema nositelju).
Podatke ćemo preuzeti s web stranica. Organizacija je sljedeća.
Ovdje je popis sastavnica: https://www.unipu.hr/sastavnice
Onda su popisi zaposlenika po sastavnici dostupni na:
https://fipu.unipu.hr/fipu/o_fakultetu/nastavnici
https://fpz.unipu.hr/fpz/o_fakultetu/nastavnici
https://fooz.unipu.hr/fooz/o_fakultetu/nastavnici
https://ffpu.unipu.hr/ffpu/o_fakultetu/nastavnici
https://mfpu.unipu.hr/mfpu/o_fakultetu/nastavnici
https://mapu.unipu.hr/mapu/o_akademiji/nastavnici
https://tfpu.unipu.hr/tfpu/o_fakultetu/nastavnici
https://fet.unipu.hr/fet/o_fakultetu/nastavnici
Svako ime je ujedno i poveznica, poveznica vodi npr na stranicu nastavnika i tu je popis kolegija, organiziran u sekciji nastava i podijeljen na prijediplomski i diplomski. Svaki kolegij je poveznica na informacije o kolegiju, koji na početku ima popis nositelja i izvođača.
U nastavku je skup funkcija u R-u za scrapeanje opisane UNIPU strukture:
Napomena: koristimo polite radi “pristojnog” dohvaćanja
(robots, rate limit), a parsiranje radimo s rvest. Funkcije
vraćaju uredne tablice (tibble) prikladne za kasniju gradnju bipartitnog
grafa u igraph.
library(polite)
library(rvest)
library(dplyr)
library(stringr)
library(purrr)
library(tidyr)
# Primjer koda generiranog uz pomoć ChatGPT-a 5.2.
#------------------------------------------------------------
# Pomoćne funkcije
#------------------------------------------------------------
txt2 <- function(x) x |> html_text2() |> str_squish()
safe_bow <- function(url, ua = "SNA-teaching (contact: your_email@unipu.hr)") {
bow(url, user_agent = ua)
}
safe_scrape <- function(session) {
tryCatch(scrape(session), error = function(e) NULL)
}
abs_url <- function(href, base) {
if (is.na(href) || href == "") return(NA_character_)
xml2::url_absolute(href, base)
}
get_component_from_url <- function(url) {
str_match(url, "unipu\\.hr/([^/]+)/")[,2]
}
canonical_staff_url <- function(url) {
comp <- str_match(url, "https?://([^\\.]+)\\.unipu\\.hr/")[,2]
slug <- str_match(url, "([a-z0-9]+\\.[a-z0-9]+)$")[,2]
if (is.na(comp) || is.na(slug)) return(NA_character_)
paste0("https://", comp, ".unipu.hr/", comp, "/", slug)
}
parse_people <- function(lines, role_label) {
if (length(lines) == 0) return(tibble(osoba = character(), detalj = character(), uloga = character()))
tibble(raw = str_squish(lines)) |>
filter(raw != ":", raw != "", !str_detect(raw, "^(Nositelji|Izvođači)\\s*:??$")) |>
transmute(
osoba = str_trim(str_remove(raw, "\\s*\\-\\s*.*$")),
detalj = str_trim(str_extract(raw, "(?<=\\-\\s).*")),
uloga = role_label
) |>
mutate(detalj = if_else(is.na(detalj), "", detalj))
}
#------------------------------------------------------------
# (1) Popis nastavnika po sastavnici -> URL-ovi profila
#------------------------------------------------------------
unipu_get_staff_urls <- function(nastavnici_url) {
component <- get_component_from_url(nastavnici_url)
ses <- safe_bow(nastavnici_url)
doc <- safe_scrape(ses)
if (is.null(doc)) {
return(tibble(staff_url = character(), staff_name = character(), component = character()))
}
base <- ses$url
a <- doc |> html_elements("a")
href <- a |> html_attr("href")
nm <- a |> txt2()
tibble(href = href, staff_name = nm) |>
mutate(url_raw = map_chr(href, abs_url, base = base)) |>
filter(!is.na(url_raw)) |>
filter(str_detect(url_raw, "([a-z0-9]+\\.[a-z0-9]+)$")) |>
mutate(staff_url = map_chr(url_raw, canonical_staff_url)) |>
filter(!is.na(staff_url)) |>
distinct(staff_url, .keep_all = TRUE) |>
mutate(component = component) |>
select(staff_url, staff_name, component)
}
#------------------------------------------------------------
# (2) Profil nastavnika -> predmeti (URL) + uloga(e) na predmetu
#------------------------------------------------------------
unipu_parse_staff_profile <- function(staff_url, component) {
ses <- safe_bow(staff_url)
doc <- safe_scrape(ses)
if (is.null(doc)) {
return(tibble(
staff_url = staff_url,
predmet_url = character(),
predmet = character(),
staff_role = character()
))
}
base <- ses$url
a <- doc |> html_elements("a")
a_txt <- a |> txt2()
a_href <- a |> html_attr("href")
predmet_links <- tibble(a_txt = a_txt, href = a_href) |>
filter(str_detect(a_txt, "\\(\\d+\\)")) |>
mutate(predmet_url = map_chr(href, abs_url, base = base)) |>
mutate(predmet = str_trim(str_remove(a_txt, "\\s*\\(\\d+\\)\\s*$"))) |>
distinct(predmet_url, .keep_all = TRUE)
li_txt <- doc |> html_elements("li") |> txt2()
li_pred <- li_txt |> keep(~ str_detect(.x, "\\(\\d+\\)"))
staff_roles <- tibble(raw = li_pred) |>
transmute(
predmet = str_trim(str_remove(raw, "\\s*\\(\\d+\\).*")),
staff_role = str_trim(str_extract(raw, "(?<=\\-\\s).*"))
)
predmet_links |>
left_join(staff_roles, by = "predmet") |>
mutate(staff_url = staff_url) |>
select(staff_url, predmet_url, predmet, staff_role)
}
unipu_parse_staff_profile_predmeti <- function(staff_url) {
ses <- safe_bow(staff_url)
doc <- safe_scrape(ses)
if (is.null(doc)) {
return(tibble(
predmet_url = character(),
predmet = character(),
predmet_comp = character()
))
}
base <- ses$url
a <- doc |> html_elements("a")
href <- a |> html_attr("href")
txt <- a |> txt2()
tibble(href = href, predmet = txt) |>
mutate(predmet_url = map_chr(href, abs_url, base = base)) |>
filter(!is.na(predmet_url)) |>
filter(str_detect(predmet_url, "unipu\\.hr/[^/]+/predmet/[a-z0-9]+$")) |>
mutate(
predmet = str_squish(predmet),
predmet_comp = get_component_from_url(predmet_url)
) |>
distinct(predmet_url, .keep_all = TRUE) |>
select(predmet_comp, predmet_url, predmet)
}
#------------------------------------------------------------
# (3) Stranica predmeta -> Nositelji i Izvođači
#------------------------------------------------------------
unipu_parse_predmet_roles <- function(predmet_url) {
ses <- safe_bow(predmet_url)
doc <- safe_scrape(ses)
comp <- get_component_from_url(predmet_url)
if (is.null(doc)) {
return(tibble(predmet_comp = comp, predmet = character(), osoba = character(), uloga = character()))
}
predmet <- doc |> html_element("h1") |> txt2()
page_txt <- doc |> html_text2() |> str_replace_all("\r", "\n")
extract_block <- function(label) {
# toleriramo i bez dvotočke
label2 <- str_remove(label, ":")
loc <- str_locate(page_txt, regex(paste0(label2, "\\s*:??"), ignore_case = FALSE))[1,]
if (any(is.na(loc))) return(character())
after <- str_sub(page_txt, loc[2] + 1)
# rezanje na idući blok (grubo, ali stabilno)
stop_loc <- str_locate(after, regex("(Nositelji|Izvođači|DETALJNE INFORMACIJE|Prijava ispita)", ignore_case = FALSE))[1,1]
if (is.na(stop_loc)) stop_loc <- nchar(after) + 1
blk <- str_sub(after, 1, stop_loc - 1)
blk |>
str_split("\n") |>
pluck(1) |>
map_chr(str_squish) |>
discard(~ .x == "" || str_detect(.x, "^(Nositelji|Izvođači)"))
}
nos <- extract_block("Nositelji:")
izv <- extract_block("Izvođači:")
parse_people <- function(lines, role_label) {
if (length(lines) == 0) return(tibble(osoba = character(), uloga = character()))
tibble(osoba = str_squish(lines), uloga = role_label)
}
bind_rows(
parse_people(nos, "Nositelj"),
parse_people(izv, "Izvođač")
) |>
mutate(
predmet_comp = comp,
predmet = predmet
) |>
select(predmet_comp, predmet, osoba, uloga)
}
#------------------------------------------------------------
# (4) End-to-end: sastavnica -> profili -> predmeti -> nositelji/izvođači
#------------------------------------------------------------
unipu_scrape_component <- function(nastavnici_url) {
staff <- unipu_get_staff_urls(nastavnici_url)
staff_predmeti <- staff |>
mutate(predmeti = map(staff_url, unipu_parse_staff_profile_predmeti)) |>
select(component, staff_url, staff_name, predmeti) |>
unnest(predmeti)
predmeti_unique <- staff_predmeti |>
distinct(predmet_url)
list(
staff_index = staff,
staff_predmeti = staff_predmeti,
predmet_roles = predmeti_unique |>
mutate(roles = map(predmet_url, unipu_parse_predmet_roles)) |>
unnest(roles) |>
select(predmet_url, everything())
)
}
urls <- c(
"https://fipu.unipu.hr/fipu/o_fakultetu/nastavnici",
"https://fpz.unipu.hr/fpz/o_fakultetu/nastavnici",
"https://fooz.unipu.hr/fooz/o_fakultetu/nastavnici",
"https://ffpu.unipu.hr/ffpu/o_fakultetu/nastavnici",
"https://mfpu.unipu.hr/mfpu/o_fakultetu/nastavnici",
"https://mapu.unipu.hr/mapu/o_akademiji/nastavnici",
"https://tfpu.unipu.hr/tfpu/o_fakultetu/nastavnici",
"https://fet.unipu.hr/fet/o_fakultetu/nastavnici")
res <- map(urls, unipu_scrape_component)
names(res) <- urls
saveRDS(res, "unipu_kolegiji_nastavnici.rds")
unipu_kolegiji_nastavnici <- readRDS("unipu_kolegiji_nastavnici.rds")
# ovdje se ne prikazuje head() niti str() zato jer ćemo prvo anonimizirati podatke
library(dplyr)
library(stringr)
library(purrr)
library(tidyr)
library(igraph)
# Pomoćne funkcije
clean_person <- function(x) {
x <- stringr::str_replace_all(x, "\u00A0", " ")
x <- stringr::str_squish(x)
x <- stringr::str_remove(x, "\\s*\\-\\s*.*$")
titule_pattern <- stringr::regex(
"(?<![\\w])(prof|doc|dr|sc|mr|izv|red|art|mag|nasl|pred|v|lekt|ing|oec|inf|comp|educ|bac|dipl|sur|umj|philol|angl|el|techn|med|dent|bibl|croat|oecol|et|prot|nat)\\.\\s*",
ignore_case = TRUE
)
repeat {
x_new <- stringr::str_remove_all(x, titule_pattern)
x_new <- stringr::str_squish(x_new)
if (identical(x_new, x)) break
x <- x_new
}
x <- stringr::str_remove_all(x, ",")
x <- stringr::str_squish(x)
x <- stringi::stri_trans_nfc(x)
x
}
extract_detail <- function(x) {
x <- str_squish(x)
det <- str_extract(x, "(?<=\\-\\s).*")
if_else(is.na(det), "", str_squish(det))
}
make_ids <- function(x, digits = 3) {
n <- length(x)
d <- digits
while (n > (10^d - 1)) d <- d + 1
tibble(
osoba_clean = x,
osoba_id = str_pad(seq_len(n), width = d, pad = "0")
)
}
# Pomoćna: svi parovi (bez self-loop), dvosmjerno kao 2 usmjerena brida
all_pairs_bidirectional <- function(ids) {
ids <- unique(ids)
if (length(ids) < 2) {
return(tibble(from = character(), to = character()))
}
cmb <- t(combn(ids, 2))
tibble(from = cmb[,1], to = cmb[,2]) |>
bind_rows(tibble(from = cmb[,2], to = cmb[,1])) |>
filter(from != to)
}
get_component_from_staff_url <- function(url) {
str_match(url, "^https?://[^/]+/([^/]+)/")[, 2]
}
get_component_from_url <- function(url) {
str_match(url, "^https?://[^/]+/([^/]+)/")[,2]
}
# Učitaj i spljošti predmet_roles
# unipu_kolegiji_nastavnici <- readRDS("unipu_kolegiji_nastavnici.rds")
predmet_roles_all <- imap_dfr(
unipu_kolegiji_nastavnici,
~ .x$predmet_roles |> mutate(source_nastavnici_url = .y)
)
predmet_roles_clean <- predmet_roles_all |>
filter(!is.na(osoba)) |>
mutate(osoba = str_squish(osoba)) |>
filter(osoba != ":", osoba != "") |>
mutate(
detalj = extract_detail(osoba),
osoba_clean = clean_person(osoba)
) |>
filter(osoba_clean != "")
# anonimizacija
osobe_unique <- predmet_roles_clean |>
distinct(osoba_clean) |>
arrange(osoba_clean) |>
pull(osoba_clean)
id_map <- make_ids(osobe_unique, digits = 3)
predmet_roles_anon <- predmet_roles_clean |>
left_join(id_map, by = "osoba_clean")
# Očisti osobe (makni ":" i normaliziraj ime)
staff_comp_map_raw <- imap_dfr(
unipu_kolegiji_nastavnici,
~ .x$staff_index %>% mutate(source_nastavnici_url = .y)
) |>
mutate(
sastavnica_nast = get_component_from_staff_url(staff_url),
staff_name_clean = clean_person(staff_name)
) |>
left_join(id_map, by = c("staff_name_clean" = "osoba_clean"))
staff_comp_map <- staff_comp_map_raw |>
filter(!is.na(osoba_id), !is.na(sastavnica_nast), sastavnica_nast != "") |>
group_by(osoba_id) |>
summarise(
sastavnica_nastavnika = paste(sort(unique(sastavnica_nast)), collapse = " | "),
n_sastavnica = n_distinct(sastavnica_nast),
.groups = "drop"
)
# Vertex tablica (detalji idu u vertex atribute) za ručnu provjeru
# - detalj može biti višestruk: spajamo sve jedinstvene detalje koje osoba ima
vertices <- predmet_roles_anon |>
group_by(osoba_id) |>
summarise(
detalj = paste(sort(unique(detalj[detalj != ""])), collapse = " | "),
osoba_clean = first(osoba_clean),
.groups = "drop"
) |>
mutate(detalj = if_else(detalj == "", NA_character_, detalj)) |>
left_join(staff_comp_map, by = "osoba_id") |>
rename(name = osoba_id)
# Potpuno anonimiziran izlaz bez imena:
predmet_roles_anon_out <- predmet_roles_anon |>
select(predmet_url, predmet_comp, predmet, uloga, osoba_id, detalj)
# Veze po predmetu: N<->N, I->N, I<->I
edges <- predmet_roles_anon |>
select(predmet_url, predmet_comp, predmet, uloga, osoba_id) |>
distinct() |>
group_by(predmet_url, predmet_comp, predmet) |>
summarise(
nositelji = list(unique(osoba_id[uloga == "Nositelj"])),
izvodaci = list(unique(osoba_id[uloga == "Izvođač"])),
.groups = "drop"
) |>
mutate(
# (a) Nositelj ↔ Nositelj
nn = map(nositelji, all_pairs_bidirectional),
# (b) Izvođač ↔ Izvođač
ii = map(izvodaci, all_pairs_bidirectional),
# (c) Izvođač → Nositelj (jednosmjerno)
in_dir = map2(izvodaci, nositelji, ~ {
if (length(.x) == 0 || length(.y) == 0) {
tibble(from = character(), to = character())
} else {
tidyr::expand_grid(from = .x, to = .y) |> filter(from != to)
}
})
) |>
transmute(
predmet_url, predmet_comp, predmet,
edges = pmap(list(nn, ii, in_dir), \(nn, ii, in_dir) bind_rows(
nn |> mutate(edge_type = "N-N"),
ii |> mutate(edge_type = "I-I"),
in_dir |> mutate(edge_type = "I->N")
))
) |>
unnest(edges) |>
filter(from != to)
Postupak konstrukcije mreže započinje objedinjavanjem svih zapisa o nositeljima i izvođačima kolegija sa svih sastavnica Sveučilišta. Izvorni zapisi se najprije čiste: uklanjaju se tehnički artefakti (npr. znak “:”), standardiziraju se imena osoba te se izdvaja eventualni dodatni opis (npr. “Seminar”, “Vježbe”). Time se osigurava da svaka osoba bude jednoznačno prepoznata bez obzira na varijacije u zapisu titula ili interpunkcije.
U sljedećem koraku provodi se anonimizacija. Svako jedinstveno ime mapira se na numerički identifikator fiksne duljine, pri čemu se ista osoba u svim zapisima pojavljuje pod istim ID-om. Time se omogućuje analiza strukture mreže bez izlaganja osobnih podataka, uz zadržavanje mogućnosti naknadne interne provjere mapiranja.
Konstrukcija bridova temelji se na logici sudjelovanja na istom kolegiju. Za svaki kolegij generiraju se tri tipa odnosa: (i) dvosmjerne veze između svih nositelja, (ii) dvosmjerne veze između svih izvođača te (iii) jednosmjerne veze od izvođača prema nositeljima. Na taj se način istodobno modelira suradnička dimenzija (simetrične veze unutar iste uloge) i hijerarhijska dimenzija odgovornosti (usmjereni odnos izvođač → nositelj).
Rezultat postupka je usmjereni graf u kojem:
Dodatno, uz pomoć podatkovnog okvira vertices, možemo izvršiti ručnu provjeru kodiranja, kao i ekstrakciju sastavnice nastavnika.
Takva struktura omogućuje paralelnu analizu gustoće, komponenti i zajednica (suradnička dimenzija), ali i analizu hijerarhije, usmjerenih tokova i pozicijske centralnosti (organizacijska dimenzija). Graf time postaje višeslojan prikaz akademske strukture – istodobno mreža suradnje i mreža odgovornosti.
unipu_sna_orig <- graph_from_data_frame(edges[, 4:5],
directed = TRUE)
# Provjera
vcount(unipu_sna_orig)
## [1] 364
ecount(unipu_sna_orig)
## [1] 8497
unipu_sna_orig
## IGRAPH 64100d3 DN-- 364 8497 --
## + attr: name (v/c)
## + edges from 64100d3 (vertex names):
## [1] 195->215 215->195 099->278 099->362 278->362 278->099 362->099 362->278
## [9] 099->347 278->347 362->347 247->328 247->174 328->174 328->247 174->247
## [17] 174->328 174->138 138->174 174->247 174->328 138->247 138->328 138->174
## [25] 299->127 159->011 159->005 159->075 159->195 159->347 159->293 159->215
## [33] 159->199 159->165 159->225 159->058 159->328 159->214 159->142 159->114
## [41] 159->052 159->289 159->281 159->181 159->336 159->247 159->064 159->098
## [49] 159->127 159->208 159->354 159->170 159->048 159->065 159->012 159->274
## [57] 011->005 011->075 011->195 011->347 011->293 011->215 011->199 011->165
## + ... omitted several edges
# Dohvatimo imena verteksa iz grafa (u redoslijedu kako ih igraph vidi)
graph_vertex_names <- V(unipu_sna_orig)$name
# Filtriramo i sortiramo vertices da odgovara grafu
vertices_aligned <- vertices |>
filter(name %in% graph_vertex_names) |>
arrange(match(name, graph_vertex_names))
vertices_aligned <- vertices_aligned |>
mutate(sastavnica_nastavnika = replace_na(sastavnica_nastavnika, "vanjski"))
# Dodjela atributa
E(unipu_sna_orig)$sastavnica <- edges$predmet_comp
E(unipu_sna_orig)$predmet <- edges$predmet
E(unipu_sna_orig)$type <- edges$edge_type
V(unipu_sna_orig)$sast_nast <- vertices_aligned$sastavnica_nastavnika
# boje
# definirani redoslijed sastavnica
unique(E(unipu_sna_orig)$sastavnica)
## [1] "fet" "ffpu" "fipu" "fooz" "mapu" "mfpu" "tfpu" "fpz"
unique(V(unipu_sna_orig)$sast_nast)
## [1] "fet" "vanjski" "fipu" "tfpu" "ffpu"
## [6] "fooz" "mfpu" "fpz" "fipu | tfpu" "mapu"
comp_levels <- c("fet","ffpu","fipu","fooz","mfpu","tfpu","fpz","mapu", "vanjski")
# prilagođena paleta
comp_cols <- c(
fet = "#7D2E1E", # terakota (crveno-smeđa)
ffpu = "darkblue", # tamno plavo
fipu = "#99CCFF", # svjetlo plavo (aqua)
fooz = "#2A9D8F", # pastelno zeleno / petrolej
mfpu = "#D62828", # crveno
tfpu = "#2E5FA3", # mornarsko plava
fpz = "#3A86FF", # plava (srednje prema svjetlijem)
mapu = "#F4A261", # narančasta
vanjski = "grey", # siva za NA
"fipu | tfpu" = "grey"
)
# dodjela boja bridovima
E(unipu_sna_orig)$col <- unname(comp_cols[E(unipu_sna_orig)$sastavnica])
V(unipu_sna_orig)$col_sast <- unname(comp_cols[V(unipu_sna_orig)$sast_nast])
# provjera
table(E(unipu_sna_orig)$sastavnica)
##
## fet ffpu fipu fooz fpz mapu mfpu tfpu
## 3129 1683 272 1710 61 475 533 634
table(E(unipu_sna_orig)$col)
##
## #2A9D8F #2E5FA3 #3A86FF #7D2E1E #99CCFF #D62828 #F4A261 darkblue
## 1710 634 61 3129 272 533 475 1683
table(V(unipu_sna_orig)$sast_nast)
##
## fet ffpu fipu fipu | tfpu fooz fpz
## 37 48 15 1 23 9
## mapu mfpu tfpu vanjski
## 16 12 10 193
table(V(unipu_sna_orig)$col_sast)
##
## #2A9D8F #2E5FA3 #3A86FF #7D2E1E #99CCFF #D62828 #F4A261 darkblue
## 23 10 9 37 15 12 16 48
## grey
## 194
set.seed(7)
lay_o <- layout_with_fr(unipu_sna_orig)
plot(unipu_sna_orig,
layout = lay_o,
vertex.size = 4,
vertex.label = NA,
vertex.color = V(unipu_sna_orig)$col_sast,
edge.color = E(unipu_sna_orig)$col,
edge.width = 0.8,
edge.arrow.size= 0.5,
edge.curved = 0.05)
E(unipu_sna_orig)$weight <- 1
unipu_sna <- igraph::simplify(
unipu_sna_orig,
remove.multiple = TRUE,
remove.loops = TRUE,
edge.attr.comb = list(
weight = "sum",
sastavnica = "first",
predmet = "first",
col = "first",
type = "first"
)
)
Nakon simplify() mreža ima 364 čvora i 3177 jedinstvenih
usmjerenih bridova, pri čemu weight označava koliko se puta isti par
osoba povezao kroz različite kolegije / pojavljivanja (agregirani
intenzitet suradnje).
set.seed(5)
lay_u <- layout_with_fr(unipu_sna)
plot(unipu_sna,
layout = lay_u,
vertex.size = 4,
vertex.label = NA,
vertex.color = V(unipu_sna)$col_sast,
edge.color = E(unipu_sna)$col,
edge.weight = E(unipu_sna)$weight,
edge.arrow.size= 0.5,
edge.curved = 0.05)
igraph::is_igraph(unipu_sna)
## [1] TRUE
igraph::is.directed(unipu_sna)
## [1] TRUE
# Broj čvorova i bridova
vcount(unipu_sna)
## [1] 364
ecount(unipu_sna)
## [1] 3177
# provjera atribute bridova i čvorova
vertex_attr_names(unipu_sna)
## [1] "name" "sast_nast" "col_sast"
edge_attr_names(unipu_sna)
## [1] "sastavnica" "predmet" "type" "col" "weight"
# Gustoća (za usmjereni graf: m / (n*(n-1)))
dens_all <- edge_density(unipu_sna, loops = FALSE)
# Komponente: za usmjerene mreže obično gledamo slabe i jake komponente
comp_weak <- igraph::components(unipu_sna, "weak")
comp_strong <- igraph::components(unipu_sna, "strong")
# Veličine komponenti
sort(comp_weak$csize, decreasing = TRUE)
## [1] 344 4 3 3 2 2 2 2 2
sort(comp_strong$csize, decreasing = TRUE)
## [1] 296 6 4 3 3 3 2 2 2 2 2 2 2 2 2 1 1 1 1
## [20] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [39] 1 1 1 1 1 1 1 1
Gustoća je 0.024 (\(≈2.4%\)) na cijeloj mreži, a 0.0267 na najvećoj slaboj komponenti. To je očekivano: iako postoji puno potencijalnih parova nastavnika \(n(n−1)\), u praksi suradnje nastaju samo kroz konkretne kolegije, pa mreža ostaje rijetka. Istodobno, gustoća nije zanemariva — ukazuje na to da postoji dovoljno preklapanja angažmana da se mreža “drži zajedno”.
# Promjer (diameter) i prosječna duljina puta imaju smisla samo na povezanim dijelovima.
# Ovdje računamo na najvećoj slaboj komponenti (giant weak component), jer je to standardno za usmjerene mreže u praksi.
giant_weak_id <- which.max(comp_weak$csize)
V_giant <- V(unipu_sna)[comp_weak$membership == giant_weak_id]
g_weak_giant <- induced_subgraph(unipu_sna, vids = V_giant)
diam_giant <- diameter(g_weak_giant, directed = TRUE, unconnected = FALSE, weights = NA)
apl_giant <- average.path.length(g_weak_giant, directed = TRUE, unconnected = FALSE)
# recipročnost i tranzitivnost
recip_all <- reciprocity(unipu_sna, ignore.loops = TRUE)
trans_all <- transitivity(as.undirected(unipu_sna, mode = "collapse"), type = "global")
# Sažetak (jedno mjesto)
basic_all <- data.frame(
n = vcount(unipu_sna),
m = ecount(unipu_sna),
density = dens_all,
reciprocity = recip_all,
transitivity_undirected = trans_all,
n_components_weak = comp_weak$no,
n_components_strong = comp_strong$no,
giant_weak_size = vcount(g_weak_giant),
giant_weak_share = vcount(g_weak_giant) / vcount(unipu_sna),
diameter_giant_weak = diam_giant,
apl_giant_weak = apl_giant
)
basic_all
## n m density reciprocity transitivity_undirected n_components_weak
## 1 364 3177 0.02404414 0.9216242 0.7379423 9
## n_components_strong giant_weak_size giant_weak_share diameter_giant_weak
## 1 46 344 0.9450549 Inf
## apl_giant_weak
## 1 Inf
Slabe komponente pokazuju 9 komponenti, ali najveća ima 344 čvora, tj. oko 94.5% svih čvorova. To znači da je većina nastavnika (prema ovoj konstrukciji) posredno povezana preko lanaca zajedničkih kolegija: i ako dvije osobe nisu nikad radile zajedno, često postoji “put” preko drugih kolega i kolegija.
Jake komponente ih je 46, a najveća ima 296 čvorova. To je tipično za usmjerene mreže: da bi čvorovi bili u istoj jako povezanoj komponenti, moraju postojati usmjereni putovi u oba smjera. Budući da smo ugradili usmjereni odnos I→N (odgovornost), dio strukture prirodno postaje “asimetričan”, pa se strong komponente fragmentiraju.
Na najvećoj slaboj komponenti dobivamo diameter \(= 12\) i prosječnu duljinu puta \(≈ 4.30\), ali na “neobrađenoj” verziji izlazi Inf. Razlog je jednostavan: i unutar najveće slabe komponente mreža nije nužno jako povezana u usmjerenom smislu, pa za neke parove čvorova ne postoji usmjereni put (npr. možemo doći od izvođača do nositelja, ali ne nužno i obrnuto). Kad ograničimo analizu na dio i postavke gdje su putovi definirani, dobivamo konačne vrijednosti.
Ove vrijednosti (\(≈4\) koraka u prosjeku, promjer \(12\)) sugeriraju da mreža ima relativno kratke “nastavne udaljenosti”: preko nekoliko posrednih suradnji možemo povezati velik dio sustava.
dens_giant <- edge_density(g_weak_giant, loops = FALSE)
recip_giant <- reciprocity(g_weak_giant, ignore.loops = TRUE)
trans_giant <- transitivity(as.undirected(g_weak_giant, mode = "collapse"), type = "global")
basic_giant <- data.frame(
n = vcount(g_weak_giant),
m = ecount(g_weak_giant),
density = dens_giant,
reciprocity = recip_giant,
transitivity_undirected = trans_giant,
diameter = diameter(g_weak_giant, directed = TRUE),
apl = average.path.length(g_weak_giant, directed = TRUE)
)
basic_giant
## n m density reciprocity transitivity_undirected diameter apl
## 1 344 3148 0.02667977 0.9218551 0.7378477 38 8.546502
Recipročnost je vrlo visoka: \(≈0.922\). To je očekivano jer smo eksplicitno generirali velik dio veza kao dvosmjerne (N–N i I–I), a i u praksi su suradnički odnosi na istom kolegiju simetrični. Preostali dio (I→N) uvodi asimetriju, ali ne dominira.
Tranzitivnost (global clustering) na neusmjerenoj projekciji je također visoka: \(≈0.738\). To znači da često vrijedi obrazac: ako A radi s B i B radi s C, onda A često radi i s C. U kontekstu kolegija to je intuitivno: nastavni timovi se ponavljaju, a angažmani se grupiraju oko istih programskih područja.
set.seed(5)
lay_giant <- layout_with_fr(g_weak_giant)
plot(
g_weak_giant,
layout = lay_giant,
vertex.size = 4,
vertex.label = NA,
vertex.color = V(g_weak_giant)$col_sast,
edge.color = E(g_weak_giant)$col,
edge.arrow.size = 0.25,
edge.width = E(g_weak_giant)$weight,
main = "UNIPU mreža (giant weak component)"
)
# ilustrativno, mreža bez vanjskih suradnika
unipu_sna_interni <- induced_subgraph(
unipu_sna,
vids = V(unipu_sna)[V(unipu_sna)$sast_nast != "vanjski"]
)
Na ovoj mreži su vanjski suradnici isključeni, pa se dobiva jasniji uvid u odnose nastavnika.
set.seed(5)
lay_inter <- layout_with_fr(unipu_sna_interni)
plot(
unipu_sna_interni,
layout = lay_inter,
vertex.size = 4,
vertex.label = NA,
vertex.color = V(unipu_sna_interni)$col_sast,
edge.color = E(unipu_sna_interni)$col,
edge.arrow.size = 0.25,
edge.width = E(unipu_sna_interni)$weight,
main = "UNIPU mreža (zaposlenici)"
)
# Napomena: većina algoritama zajednica radi na neusmjerenoj projekciji.
g_giant_u <- as.undirected(g_weak_giant, mode = "collapse", edge.attr.comb = "first")
# Louvain (brz i često dobar za veće mreže)
set.seed(5)
comm_louv <- cluster_louvain(g_giant_u)
# Walktrap (alternativa; može biti sporiji)
set.seed(5)
comm_wt <- cluster_walktrap(g_weak_giant, steps = 4)
# Veličine zajednica
sizes_louv <- sort(sizes(comm_louv), decreasing = TRUE)
sizes_wt <- sort(sizes(comm_wt), decreasing = TRUE)
# Modularnost (kvaliteta particije)
mod_louv <- modularity(comm_louv)
mod_wt <- modularity(comm_wt)
community_summary <- tibble(
method = c("louvain", "walktrap"),
n_communities = c(length(sizes_louv), length(sizes_wt)),
modularity = c(mod_louv, mod_wt),
largest_community_size = c(sizes_louv[1], sizes_wt[1])
)
community_summary
## # A tibble: 2 × 4
## method n_communities modularity largest_community_size
## <chr> <int> <dbl> <int>
## 1 louvain 11 0.731 63
## 2 walktrap 22 0.726 64
# spremimo pripadnost zajednici kao atribut čvora (na giant component grafu)
V(g_weak_giant)$comm_louv <- membership(comm_louv)
V(g_weak_giant)$comm_wt <- membership(comm_wt)
# Brzi prikaz zajednica (na giant component, ali bojanje čvorova po zajednici)
base_cols <- c('#a6cee3','#1f78b4','#b2df8a','#33a02c','#fb9a99','#e31a1c',
'#fdbf6f','#ff7f00','#cab2d6','#6a3d9a','#ffff99'
)
# iz https://colorbrewer2.org/#type=qualitative&scheme=Paired&n=9
comm_ids <- sort(unique(V(g_weak_giant)$comm_louv))
V(g_weak_giant)$col_louv <- base_cols[V(g_weak_giant)$comm_louv]
plot(
g_weak_giant,
layout = lay_giant,
vertex.size = 4,
vertex.label = NA,
vertex.color = V(g_weak_giant)$col_louv,
edge.color = E(g_weak_giant)$col,
edge.arrow.size = 0.25,
main = "Zajednice na giant component (Louvain; čvorovi obojani po zajednici)"
)
Na najvećoj komponenti Louvain daje 11 zajednica uz modularnost \(≈0.725\), a Walktrap 22 zajednice uz modularnost \(≈0.706\). Oba rezultata upućuju na snažnu modularnu strukturu: postoje jasne “grupe” koje su gusto povezane iznutra.
U ovom kontekstu te zajednice ne interpretiramo kao neformalnu strukturu, nego kao grupiranje po kompetencijama/ stručnosti i nastavnim područjima (tko tipično zajedno izvodi kolegije istog sadržajnog profila). Razlika u broju zajednica između algoritama je normalna: Walktrap često daje finiju segmentaciju, dok Louvain teži “krupnijim” blokovima.
Koja su preklapanja formalne (sastavnice) i strukovne (zajednice) strukture?
table(sastavnica = V(g_weak_giant)$sast_nast,
zajednica = V(g_weak_giant)$comm_louv) |>
as.data.frame() |>
ggplot(aes(x = zajednica, y = sastavnica, fill = Freq)) +
geom_tile() +
geom_text(aes(label = Freq)) +
scale_fill_gradient(low = "white", high = "#1A5276") +
theme_minimal()
Vizualizacija razlike između formalne (sastavnice) i strukovne (zajednice) strukture:
# Sankey / alluvial dijagram - koja zajednica odgovara kojoj sastavnici
library(ggalluvial)
comp_comm_df <- tibble(
sastavnica = V(g_weak_giant)$sast_nast,
zajednica = as.character(V(g_weak_giant)$comm_louv)
) |>
count(sastavnica, zajednica)
ggplot(comp_comm_df, aes(axis1 = sastavnica, axis2 = zajednica, y = n)) +
geom_alluvium(aes(fill = sastavnica)) +
geom_stratum() +
geom_text(stat = "stratum", aes(label = after_stat(stratum))) +
theme_minimal()
Udio veza između i unutar sastavnica:
edges_sast <- igraph::as_data_frame(g_weak_giant, what = "edges") |>
select(from, to) |> # uzmi samo from/to, preskoči problematične atribute
left_join(tibble(from = V(g_weak_giant)$name, sast_from = V(g_weak_giant)$sast_nast), by = "from") |>
left_join(tibble(to = V(g_weak_giant)$name, sast_to = V(g_weak_giant)$sast_nast), by = "to") |>
mutate(tip = if_else(sast_from == sast_to, "unutar", "između"))
# Ukupni udio
edges_sast |> count(tip) |> mutate(udio = n / sum(n))
## tip n udio
## 1 između 1712 0.5438374
## 2 unutar 1436 0.4561626
# Po sastavnici
edges_sast |>
group_by(sast_from, tip) |>
summarise(n = n(), .groups = "drop") |>
group_by(sast_from) |>
mutate(udio = n / sum(n)) |>
pivot_wider(names_from = tip, values_from = c(n, udio), values_fill = 0)
## # A tibble: 10 × 5
## # Groups: sast_from [10]
## sast_from n_između n_unutar udio_između udio_unutar
## <chr> <int> <int> <dbl> <dbl>
## 1 fet 294 527 0.358 0.642
## 2 ffpu 208 343 0.377 0.623
## 3 fipu 89 28 0.761 0.239
## 4 fipu | tfpu 4 0 1 0
## 5 fooz 153 118 0.565 0.435
## 6 fpz 36 9 0.8 0.2
## 7 mapu 27 19 0.587 0.413
## 8 mfpu 52 35 0.598 0.402
## 9 tfpu 75 31 0.708 0.292
## 10 vanjski 774 326 0.704 0.296
Kad gledamo bridove po sastavnici nastavnika (atribut čvora), dobivamo da je ukupno više veza između sastavnica (\(≈54.4%\)) nego unutar (\(≈45.6%\)). To je uvelike vezano uz angažman vanjskih suradnika, koji kao “vanjski” predstavljaju zasebnu skupinu (sastavnicu). No, vanjski suradnici imaju tendenciju raditi na više sastavnica, kao i mnogi nastavnici i suradnici, što sugerira da se velik dio nastave oslanja na među-sastavničko povezivanje i vanjske suradnike.
Na razini pojedinih sastavnica vidimo različite obrasce. Npr. FET i FFPU imaju veći udio veza unutar (\(≈0.62–0.64\)), dok FIPU, TFPU i FPZ imaju znatno veći udio suradnji između sastavnica (\(≈0.71–0.80\)). To je konzistentno s idejom da neke sastavnice većinu nastave organiziraju interno, dok su druge orijentirane na suradnju i interdisciplinarnost.
Udio veza između i unutar zajednica:
edges_comm <- igraph::as_data_frame(g_weak_giant, what = "edges") |>
select(from, to) |>
left_join(tibble(from = V(g_weak_giant)$name, comm_from = as.character(V(g_weak_giant)$comm_louv)), by = "from") |>
left_join(tibble(to = V(g_weak_giant)$name, comm_to = as.character(V(g_weak_giant)$comm_louv)), by = "to") |>
mutate(tip = if_else(comm_from == comm_to, "unutar", "između"))
# Ukupni udio
edges_comm |> count(tip) |> mutate(udio = n / sum(n))
## tip n udio
## 1 između 226 0.07179161
## 2 unutar 2922 0.92820839
# Po zajednici
edges_comm |>
group_by(comm_from, tip) |>
summarise(n = n(), .groups = "drop") |>
group_by(comm_from) |>
mutate(udio = n / sum(n)) |>
pivot_wider(names_from = tip, values_from = c(n, udio), values_fill = 0)
## # A tibble: 11 × 5
## # Groups: comm_from [11]
## comm_from n_između n_unutar udio_između udio_unutar
## <chr> <int> <int> <dbl> <dbl>
## 1 1 50 1121 0.0427 0.957
## 2 10 12 48 0.2 0.8
## 3 11 3 34 0.0811 0.919
## 4 2 22 99 0.182 0.818
## 5 3 33 555 0.0561 0.944
## 6 4 12 23 0.343 0.657
## 7 5 14 226 0.0583 0.942
## 8 6 20 55 0.267 0.733
## 9 7 17 215 0.0733 0.927
## 10 8 35 508 0.0645 0.936
## 11 9 8 38 0.174 0.826
Konceptualno, očekujemo da udio unutar-zajednice bude visok (jer modularnost \(≈0.72\)). Napomena: ovdje uistinu vidimo algoritam detekcije zajednica na djelu: nije važno koja je formalna podjela po sastavnicama, promatraju se suradnje na zajedničkim kolegijima.
Prelazimo na centralnost.
cent <- tibble(
name = V(g_weak_giant)$name,
osoba_clean = V(g_weak_giant)$osoba_clean,
sast_nast = V(g_weak_giant)$sast_nast,
degree = igraph::degree(g_weak_giant, mode = "all"),
in_degree = igraph::degree(g_weak_giant, mode = "in"),
out_degree = igraph::degree(g_weak_giant, mode = "out"),
closeness = igraph::closeness(g_weak_giant, mode = "all", normalized = TRUE),
betweenness = igraph::betweenness(g_weak_giant, normalized = TRUE),
eigenvector = igraph::eigen_centrality(g_weak_giant, directed = TRUE)$vector,
pagerank = igraph::page_rank(g_weak_giant)$vector,
hits_auth = igraph::authority_score(g_weak_giant)$vector,
hits_hub = igraph::hub_score(g_weak_giant)$vector
)
cent |> arrange(desc(eigenvector))
## # A tibble: 344 × 11
## name sast_nast degree in_degree out_degree closeness betweenness eigenvector
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 274 fet 66 33 33 0.184 0.000208 1
## 2 181 vanjski 66 33 33 0.184 0.000216 0.919
## 3 165 fet 66 33 33 0.184 0.000216 0.911
## 4 208 fet 72 37 35 0.180 0.00390 0.800
## 5 114 vanjski 68 34 34 0.179 0.00323 0.763
## 6 011 fet 66 33 33 0.185 0.00561 0.759
## 7 170 fet 66 33 33 0.183 0.00286 0.711
## 8 159 vanjski 66 33 33 0.178 0.000581 0.686
## 9 064 fet 69 35 34 0.186 0.0119 0.651
## 10 293 vanjski 66 33 33 0.183 0.000879 0.649
## # ℹ 334 more rows
## # ℹ 3 more variables: pagerank <dbl>, hits_auth <dbl>, hits_hub <dbl>
U ovoj mreži “važnost” može značiti različite stvari, pa je smisleno gledati više mjera:
Degree / in-degree / out-degree: osobe koje sudjeluju u puno kolegija ili u puno timova. U ovom modelu in/out može djelomično reflektirati uloge (izvođač→nositelj uvodi usmjerenost), ali zbog simetričnih veza i agregacije treba tumačiti oprezno.
Betweenness: osobe koje povezuju različite nastavne skupine — “mostovi” između područja ili između sastavnica. U praksi su to često osobe koje sudjeluju na kolegijima koji spajaju programe (interdisciplinarno područje), ili imaju angažmane na više mjesta.
Closeness: osobe koje su “blizu svima” u smislu prosječne udaljenosti — tipično dobro integrirane u sustav suradnji.
Eigenvector / PageRank: osobe povezane s drugim “dobro povezanim” osobama, tj. u središtu gustih, utjecajnih nastavnih klastera. To često ne znači “najviše kolegija”, nego “najcentralnije mjesto u mreži suradnje”.
HITS (hub/authority): u usmjerenim mrežama može dati nijansiranije uloge (hub kao “izvor prema autoritetima”, authority kao “cilj mnogih”), ali ovdje usmjerenost djelomično dolazi iz konstrukcije (I→N), pa rezultate treba tumačiti kao strukturne uloge u modelu odgovornosti, ne kao “stvarni autoritet” izvan definicije mreže.
par(mfrow=c(3,3))
V(g_weak_giant)$degree <- cent$degree
V(g_weak_giant)$indegree <- cent$in_degree
V(g_weak_giant)$outdegree <- cent$out_degree
V(g_weak_giant)$close <- cent$closeness
V(g_weak_giant)$betwe <- cent$betweenness
V(g_weak_giant)$eigen <- cent$eigenvector
V(g_weak_giant)$pagerank <- cent$pagerank
V(g_weak_giant)$auth <- cent$hits_auth
V(g_weak_giant)$hub <- cent$hits_hub
plot(
g_weak_giant,
layout = lay_giant,
vertex.size = V(g_weak_giant)$degree/5,
vertex.label = NA,
vertex.color = V(g_weak_giant)$col_sast,
edge.color = E(g_weak_giant)$col,
edge.arrow.size = 0.25,
edge.width = E(g_weak_giant)$weight,
main = "UNIPU mreža (giant weak component) \n veličina čvora = degree"
)
plot(
g_weak_giant,
layout = lay_giant,
vertex.size = V(g_weak_giant)$indegree/3,
vertex.label = NA,
vertex.color = V(g_weak_giant)$col_sast,
edge.color = E(g_weak_giant)$col,
edge.arrow.size = 0.25,
edge.width = E(g_weak_giant)$weight,
main = "UNIPU mreža (giant weak component) \n veličina čvora = indegree"
)
plot(
g_weak_giant,
layout = lay_giant,
vertex.size = V(g_weak_giant)$outdegree/3,
vertex.label = NA,
vertex.color = V(g_weak_giant)$col_sast,
edge.color = E(g_weak_giant)$col,
edge.arrow.size = 0.25,
edge.width = E(g_weak_giant)$weight,
main = "UNIPU mreža (giant weak component) \n veličina čvora = outdegree"
)
plot(
g_weak_giant,
layout = lay_giant,
vertex.size = V(g_weak_giant)$close*20,
vertex.label = NA,
vertex.color = V(g_weak_giant)$col_sast,
edge.color = E(g_weak_giant)$col,
edge.arrow.size = 0.25,
edge.width = E(g_weak_giant)$weight,
main = "UNIPU mreža (giant weak component) \n veličina čvora = closeness"
)
plot(
g_weak_giant,
layout = lay_giant,
vertex.size = V(g_weak_giant)$betwe*200,
vertex.label = NA,
vertex.color = V(g_weak_giant)$col_sast,
edge.color = E(g_weak_giant)$col,
edge.arrow.size = 0.25,
edge.width = E(g_weak_giant)$weight,
main = "UNIPU mreža (giant weak component) \n veličina čvora = betweenness"
)
plot(
g_weak_giant,
layout = lay_giant,
vertex.size = V(g_weak_giant)$eigen*20,
vertex.label = NA,
vertex.color = V(g_weak_giant)$col_sast,
edge.color = E(g_weak_giant)$col,
edge.arrow.size = 0.25,
edge.width = E(g_weak_giant)$weight,
main = "UNIPU mreža (giant weak component) \n veličina čvora = eigenvector"
)
plot(
g_weak_giant,
layout = lay_giant,
vertex.size = V(g_weak_giant)$pagerank*200,
vertex.label = NA,
vertex.color = V(g_weak_giant)$col_sast,
edge.color = E(g_weak_giant)$col,
edge.arrow.size = 0.25,
edge.width = E(g_weak_giant)$weight,
main = "UNIPU mreža (giant weak component) \n veličina čvora = page rank"
)
plot(
g_weak_giant,
layout = lay_giant,
vertex.size = V(g_weak_giant)$auth*20,
vertex.label = NA,
vertex.color = V(g_weak_giant)$col_sast,
edge.color = E(g_weak_giant)$col,
edge.arrow.size = 0.25,
edge.width = E(g_weak_giant)$weight,
main = "UNIPU mreža (giant weak component) \n veličina čvora = authority"
)
plot(
g_weak_giant,
layout = lay_giant,
vertex.size = V(g_weak_giant)$hub*20,
vertex.label = NA,
vertex.color = V(g_weak_giant)$col_sast,
edge.color = E(g_weak_giant)$col,
edge.arrow.size = 0.25,
edge.width = E(g_weak_giant)$weight,
main = "UNIPU mreža (giant weak component) \n veličina čvora = hub"
)
Koncept ekvivalencije uvodi kvalitativno drukčiji pogled na mrežu od onoga koji nude mjere centralnosti. Dok centralnosti rangiraju aktere prema “važnosti”, ekvivalencije odgovaraju na pitanje tko je s kim zamjenjiv u strukturi odnosa. Time prelazimo s individualnih mjera na pozicije, a iz pozicija proizlaze uloge. U klasičnoj literaturi razlikuju se tri temeljna oblika ekvivalencije: strukturna, automorfna i regularna, pri čemu svaka sljedeća predstavlja općenitiji (i teorijski zahtjevniji) koncept.
Enronova mreža e-pošte je dobar kontrast UNIPU mreži jer je relacija
prirodno usmjerena (pošiljatelj → primatelj) i često odražava
organizacijske tokove (naredbe, izvještavanje, koordinacija). Atribut
V(enron)$Note daje “formalnu” funkciju (npr. Vice
President, Director, Employee), ali je poanta ove cjeline pokazati da
formalna funkcija nije isto što i pozicija. Regularna ekvivalencija
upravo hvata “biti isti tip aktera” u smislu povezivanja s ekvivalentnim
tipovima (rekurzivna definicija uloga), dok je strukturna ekvivalencija
stroža i traži (gotovo) iste susjede; Van Steen (2010) eksplicitno uvodi
automorfnu ekvivalenciju kao topološku simetriju te regularnu
ekvivalenciju kao ulogu definiranu kroz veze prema ekvivalentnima.
Strukturna ekvivalencija je najstroži oblik. Dva aktera \(i\) i \(j\) strukturno su ekvivalentna ako imaju identične veze prema svim ostalim akterima u mreži. U matrici susjedstva to znači da su njihovi retci (i stupci, kod usmjerenih mreža) identični.
Formalno, u binarnoj mreži vrijedi:
\[ i \equiv_S j \iff x_{ik} = x_{jk} \ \text{i} \ x_{ki} = x_{kj} \ \forall k \]
gdje je \(x_{ik}\) element matrice susjedstva koji označava postoji li veza od \(i\) prema \(k\). Drugim riječima, za svaki treći čvor \(k\), akteri \(i\) i \(j\) moraju imati isti obrazac veza.
Ovaj kriterij je vrlo restriktivan i u empirijskim mrežama rijetko daje savršene podudarnosti. Zbog toga se u praksi koristi pojam strukturne sličnosti, gdje se identičnost zamjenjuje mjerama udaljenosti ili korelacije između redaka matrice susjedstva (npr. Euklidska udaljenost ili Pearsonova korelacija), što omogućuje grupiranje aktera u približno ekvivalentne pozicije (Wasserman & Faust, 1994; Jackson, 2008).
Strukturna ekvivalencija intuitivno odgovara situacijama u kojima su akteri potpuno zamjenjivi — primjerice, dva zaposlenika koji imaju istog nadređenog i surađuju s istim kolegama.
g_giant)U Enron mreži strukturna ekvivalencija bila bi situacija u kojoj dva zaposlenika šalju e-mailove točno istim osobama i primaju e-mailove od točno istih osoba.
S obzirom na to da je riječ o realnoj organizaciji s 182 čvora i 3010 agregiranih veza, takva savršena podudarnost je iznimno rijetka. Čak i zaposlenici istog odjela obično imaju barem jednu komunikacijsku razliku (drugi projekt, druga adresa, druga distribucijska lista).
Ono što u praksi možemo očekivati nisu savršene podudarnosti, nego visoke korelacije redaka matrice susjedstva. To bi moglo identificirati npr.:
administrativne asistente koji komuniciraju s istim nadređenima,
članove malog projektnog tima,
formalno paralelne pozicije u hijerarhiji.
Dakle, u Enronu je strukturna ekvivalencija interpretativno bliska ideji potpune zamjenjivosti u komunikacijskoj ulozi, ali empirijski se pojavljuje rijetko.
Za ekvivalencije je najčišće krenuti od binarne matrice susjedstva (postoji / ne postoji veza). Težine (npr. broj mailova) se mogu koristiti kasnije, ali klasične definicije ekvivalencija su binarne.
library(igraph)
library(sna)
# Helper: binariziraj graf (zadržava smjer, uklanja multi/loop ako treba)
to_binary_simple <- function(g, remove_loops = TRUE) {
g2 <- g
# ako postoji weight, sve >0 tretiramo kao 1
if ("weight" %in% edge_attr_names(g2)) {
E(g2)$weight <- as.numeric(E(g2)$weight > 0)
} else {
E(g2)$weight <- 1
}
g2 <- igraph::simplify(
g2,
remove.multiple = TRUE,
remove.loops = remove_loops,
edge.attr.comb = list(weight = "sum") # u binarnom slučaju ostaje 1 po paru
)
# nakon simplify: opet binariziraj (ako se sumalo)
E(g2)$weight <- as.numeric(E(g2)$weight > 0)
g2
}
gE <- to_binary_simple(g_giant) # Enron giant
Savršena strukturna ekvivalencija je rijetka - trebamo pronaći parove s identičnim obrascem veza. U usmjerenoj mreži tražimo da su i izlazni i ulazni obrasci isti. To možemo provjeriti direktno preko matrice susjedstva.
vnames_safe <- function(g) {
nm <- igraph::vertex_attr(g, "name")
if (is.null(nm)) nm <- igraph::as_ids(V(g)) # moramo ubaciti pomoćnu funkciju za Enron, jer nema "imena" čvorova
as.character(nm)
}
structural_equiv_pairs_exact <- function(g, top_n = 20) {
A <- as.matrix(as_adj(g, sparse = FALSE)) # out obrasci
Ain <- t(A) # in obrasci
# “potpis” čvora: out + in
sig <- apply(cbind(A, Ain), 1, paste0, collapse = "")
vn <- vnames_safe(g)
groups <- split(vn, sig)
groups <- groups[sapply(groups, length) > 1]
groups <- groups[order(sapply(groups, length), decreasing = TRUE)]
head(groups, top_n)
}
se_exact_enron <- structural_equiv_pairs_exact(gE)
se_exact_enron
## $...
## [1] "53" "87"
U Enron mreži, stroga strukturna ekvivalencija znači da bi dva zaposlenika morala imati potpuno identičan obrazac slanja i primanja poruka prema svim ostalim zaposlenicima u promatranoj mreži. Budući da je riječ o usmjerenoj mreži, kriterij se odnosi istodobno na redak i stupac matrice susjedstva, tj. na OUT i IN profil. U organizacijskoj komunikaciji takva bi situacija implicirala gotovo potpunu zamjenjivost u komunikacijskoj funkciji (isti primatelji, isti pošiljatelji), što je teorijski razumljivo, ali u praksi teško ostvarivo čak i među formalno “paralelnim” radnim mjestima.
Empirijski rezultat ukazuje na to da se pojavljuju tek izolirane i malobrojne skupine čvorova s identičnim potpisom (npr. pronađeni parovi) i konzistentan je s očekivanjem za realnu, heterogenu organizacijsku mrežu. Čak i u slučajevima gdje postoje standardizirani tokovi (distribucijske liste, administrativna podrška, rutinizirana izvješća), dovoljno je da se jedan dodatni kontakt pojavi samo kod jednog člana para da bi stroga ekvivalencija nestala. Stoga nalaz treba čitati kao potvrdu da je “egzaktna” strukturna ekvivalencija u Enronu iznimka, a ne pravilo te da je analitički produktivnije prijeći na strukturnu sličnost (udaljenosti/korelacije) i klasteriranje pozicija.
Ako se takvi rijetki ekvivalentni parovi interpretiraju, najvjerojatniji mehanizmi su institucionalna standardizacija komunikacije (npr. zajedničke liste ili automatizirani obrasci) ili vrlo usko podijeljeni zadaci u kojima dvije adrese funkcioniraju kao gotovo identične “točke ulaza/izlaza” za isti skup interakcija. Međutim, bez dodatnih atributa (tim, uloga, funkcija, mailbox vs osoba) te se interpretacije moraju zadržati na razini strukturne mogućnosti, a ne činjenične atribucije.
g_weak_giant)U UNIPU mreži strukturna ekvivalencija znači da dvije osobe sudjeluju na točno istim kolegijima i s točno istim suradnicima, uz istu kombinaciju uloga (N–N, I–I, I→N).
Takve situacije su realnije nego u Enronu — primjerice:
dva izvođača koji uvijek rade zajedno na istom skupu kolegija,
dva nositelja koji su supotpisnici istih kolegija.
Ipak, i ovdje je kriterij vrlo restriktivan. Čim se pojavi jedan dodatni kolegij ili drugačiji tim, identičnost nestaje.
gU <- to_binary_simple(g_weak_giant) # UNIPU giant weak
se_exact_unipu <- structural_equiv_pairs_exact(gU)
se_exact_unipu
## $...
## [1] "105" "222"
##
## $...
## [1] "239" "056"
##
## $...
## [1] "144" "149"
##
## $...
## [1] "212" "231"
U UNIPU mreži, stroga strukturna ekvivalencija znači da bi dvije osobe morale imati identičan obrazac veza prema svim ostalim osobama u mreži, i to uzimajući u obzir smjer veze (u mreži koja uključuje i suradničku i hijerarhijsku komponentu). U praksi to bi se moglo dogoditi u stabilnim nastavnim konfiguracijama gdje se dva aktera kroz promatrano razdoblje pojavljuju u potpuno istim nastavnim timovima i u istim relacijama prema ostalima. U takvom slučaju, ekvivalencija ne bi nužno bila “osobna zamjenjivost”, nego zamjenjivost u smislu strukturalnog položaja u nastavnoj raspodjeli rada (npr. u slučaju produženog bolovanja jedne osobe, druga osoba može uskočiti bez velikih promjena ili posljedica za nastavni proces, organizaciju i suradnje).
Nalaz više egzaktno ekvivalentnih parova u UNIPU mreži (u odnosu na Enron) metodološki je očekivan jer je relacija u toj mreži često vezana uz ponavljajuće institucionalne obrasce (nastavni timovi, uloge nositelja/izvođača, stabilne suradnje). Ipak, i ovdje je kriterij vrlo restriktivan: dovoljno je da se jedna osoba pojavi na dodatnom kolegiju ili u drugačijoj kombinaciji suradnika pa potpuna identičnost profila nestaje. Zbog toga se i u UNIPU slučaju stroga strukturna ekvivalencija tipično pojavljuje kao lokalni fenomen (manje skupine/parovi), a ne kao dominantan princip organizacije mreže.
Interpretativno, egzaktna strukturna ekvivalencija u UNIPU mreži najčešće bi upućivala na vrlo sličnu “nastavnu portfeljnu strukturu” dvaju aktera u promatranom razdoblju. U kontekstu pozicijske analize to je korisno kao demonstracija najstrožeg kriterija, ali analitički fokus se u pravilu prebacuje na strukturnu sličnost i regularnu ekvivalenciju, jer one bolje zahvaćaju funkcionalnu diferencijaciju sustava i stabilne uloge koje se ponavljaju kroz različite timove.
U obje mreže stroga strukturna ekvivalencija ostaje rijetka jer je “identičnost prema svima” ekstremno zahtjevan uvjet. Razlika je u tome što se u UNIPU mreži češće pojavljuju ponavljajući institucionalni obrasci koji mogu proizvesti identične profile (npr. stabilni timovi i uloge), dok Enronova komunikacija odražava veću heterogenost i nepodudarnost kontakata, pa se egzaktne podudarnosti pojavljuju tek sporadično. Zaključujemo da je u obje mreže strukturna ekvivalencija teorijski jasna, ali empirijski rijetka, te je u praksi zamjenjujemo mjerama strukturne sličnosti.
Automorfna ekvivalencija ublažava zahtjev identičnih susjeda. Dva aktera su automorfno ekvivalentna ako postoji preslikavanje (automorfizam) grafa na samoga sebe koje jednog aktera preslikava u drugog, a da pritom struktura grafa ostaje nepromijenjena (Van Steen, 2010).
Intuitivno, akteri su automorfno ekvivalentni ako zauzimaju simetrične pozicije u strukturi, iako nisu nužno povezani s istim osobama. Primjer je lančana struktura \(A\)–\(B\)–\(C\)–\(D\): čvorovi \(A\) i \(D\) su automorfno ekvivalentni jer zauzimaju simetrične krajnje pozicije, iako nemaju iste susjede.
Ovaj oblik ekvivalencije oslanja se na graf-teorijsku simetriju i naglašava topološku poziciju, a ne konkretne relacije. Time se uvodi važna distinkcija: strukturna ekvivalencija promatra identitet veza, dok automorfna promatra identitet mjesta u strukturi.
Pojednostavljeno, razdvaja “isti susjedi” od “isto mjesto u strukturi”, ali je u empirijskim društvenim mrežama često ograničene interpretativne snage jer je osjetljiva na granice mreže i lokalne topološke detalje.
U komunikacijskoj mreži automorfna ekvivalencija traži topološku simetriju, a ne identične kontakte.
Npr.:
dva zaposlenika na rubu mreže koji komuniciraju samo s jednim nadređenim,
dva člana različitih odjela koji zauzimaju istu “poziciju” u lokalnoj strukturi (npr. oba su posrednici između dva klastera).
Iako možda ne komuniciraju s istim osobama, njihova strukturna uloga u grafu je simetrična.
U Enronu se automorfne ekvivalencije mogu pojaviti u manjim podstrukturama (lokalne simetrije), ali u cijeloj mreži rijetko nalazimo globalne automorfizme zbog kompleksnosti i heterogenosti komunikacije.
library(igraph)
automorphism_size <- function(g) {
gE <- as.undirected(g, mode = "collapse")
grp <- automorphism_group(gE, details = FALSE)
grp
}
aut_enron <- automorphism_size(gE)
str(aut_enron)
## List of 2
## $ : 'igraph.vs' int [1:182] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bca9744-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' int [1:182] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bca9744-1648-11f1-8000-010000000000"
aut_enron označava skup svih permutacija čvorova koje
preslikavaju graf u samoga sebe, a da pritom struktura ostaje identična.
Output sadrži:
veličinu grupe (broj automorfizama),
generatore grupe (ako tražite details = TRUE),
informacije o simetričnim strukturama.
Najvažnija informacija je veličina grupe.
Automorfna ekvivalencija u Enron mreži znači da se dva čvora mogu zamijeniti permutacijom čvorova koja čuva cijelu strukturu grafa. U takvoj definiciji nije važno jesu li akteri povezani s istim osobama, nego zauzimaju li simetrično mjesto u topologiji mreže. U empirijskoj komunikacijskoj mreži organizacije, globalne simetrije su tipično rijetke jer je komunikacija snažno obilježena funkcionalnim razlikama (specijalizirane uloge, različiti timovi, razlike u intenzitetu komunikacije), pa čak i “slični” akteri najčešće nisu topološki zamjenjivi na razini cijele mreže.
Rezultat dobiven funkcijom automorphism_group() u takvom
se kontekstu obično interpretira kao pokazatelj koliko je mreža
strukturno asimetrična. Dobivena vrlo mala automorfna struktura (tj.
dvije netrivijalne permutacije ili praktično samo trivijalni
automorfizam), to je očekivano: mreža je dovoljno heterogena da gotovo
svaki čvor ima jedinstven “otisak” u strukturi. Da su se pojavile
automorfne klase, onda bismo ih tumačili u kontekstu njihove povezanosti
s lokalnim simetrijama (npr. više perifernih čvorova koji su vezani na
isti način prema istom dijelu grafa, ili ponavljajuće “list” strukture).
U organizacijskoj interpretaciji, takve simetrije više govore o tome da
je dio mreže organiziran kroz ponavljajući obrazac (npr. rubni akteri s
jednim dominantnim kontaktom), nego da su akteri u stvarnosti međusobno
zamjenjivi u smislu uloge.
UNIPU mreža ima izraženiju modularnost (\(≈0.72\)), pa se automorfna ekvivalencija može intuitivnije uočiti.
Primjeri:
dva izvođača koji svaki u svojoj sastavnici rade s jednim nositeljem i nemaju druge veze,
dva “periferna” člana različitih zajednica koji su simetrično povezani prema centru svoje grupe.
Ovdje automorfna ekvivalencija ilustrira simetriju pozicija unutar kompetencijskih klastera, neovisno o formalnoj sastavnici.
aut_unipu <- automorphism_size(gU)
str(aut_unipu)
## List of 45
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 17 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "199" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
## $ : 'igraph.vs' Named int [1:344] 1 2 3 4 5 6 7 8 9 10 ...
## ..- attr(*, "names")= chr [1:344] "195" "215" "099" "278" ...
## ..- attr(*, "env")=<weakref>
## ..- attr(*, "graph")= chr "7bd2b87a-1648-11f1-8000-010000000000"
U UNIPU dijelu se vidi velika lista (“List of 45”), ali to nije nužno ‘mnogo simetrije’ u sociološkom smislu; često znači da postoje orbite/permutacije (koliko “tipova simetričnih pozicija” postoji) jer graf sadrži puno čvorova s vrlo sličnom lokalnom strukturom (npr. više perifernih čvorova vezanih na sličan način), ili zato što koristimo neusmjerenu inačicu grafa (simetrije porastu kad se smjer ignorira).
U UNIPU mreži automorfna ekvivalencija također traži topološku zamjenjivost, ali je vjerojatnost pojave simetrija nešto veća jer je mreža sastavljena od institucionalnih obrazaca koji se ponavljaju. Npr., više “perifernih” aktera može biti povezano na vrlo sličan način prema vlastitom lokalnom klasteru, ili se mogu pojaviti ponavljajuće mikro-strukture (npr. više izvođača s usporedivim vezama prema nositeljima i ograničenim brojem drugih veza). Takvi slučajevi stvaraju simetrične konfiguracije koje su pogodne za automorfizme, iako akteri nisu nužno povezani s istim osobama.
Empirijski rezultat u kojem automorphism_group() vraća
bogatiji skup generatora ili više klasa može se interpretirati kao
indikator da u mreži postoji više topoloških “šablona” koji se
ponavljaju, najčešće unutar modularnih dijelova mreže. Važno je
naglasiti da automorfna ekvivalencija ovdje nije isto što i funkcionalna
uloga (nositelj/izvođač), nego prije pokazuje da određeni dijelovi mreže
imaju strukturnu simetriju. To je ilustrativno korisno jer lijepo
razlikuje “isti susjedi” (strukturna) od “isto mjesto u obliku”
(automorfna), ali je za sociološki smislenije “uloge” UNIPU sustava
automorfna ekvivalencija obično preslaba (previše topološka) i
preosjetljiva na granice mreže i detalje konstrukcije brida.
Regularna ekvivalencija je najopćenitiji i sociološki najznačajniji oblik. Dva aktera su regularno ekvivalentna ako imaju istu vrstu odnosa prema akterima koji su i sami ekvivalentni. Definicija je rekurzivna i ne zahtijeva identične susjede.
Formalno, vrijedi:
\[ i \equiv_R j \iff \begin{cases} \forall k: x_{ik} = 1 \Rightarrow \exists l \text{ takav da } x_{jl} = 1 \text{ i } k \equiv_R l \ \forall l: x_{jl} = 1 \Rightarrow \exists k \text{ takav da } x_{ik} = 1 \text{ i } l \equiv_R k \end{cases} \]
Ovdje \(x_{ik}\) označava postojanje veze, a relacija \(\equiv_R\) označava regularnu ekvivalenciju. Drugim riječima, akteri \(i\) i \(j\) nisu povezani s istim osobama, nego s osobama koje zauzimaju istu poziciju.
Primjer je obrazovni sustav: svi profesori su regularno ekvivalentni jer predaju studentima, iako predaju različitim grupama studenata; svi studenti su regularno ekvivalentni jer primaju znanje od profesora. Ovdje se prvi put jasno pojavljuje pojam uloge. Regularna ekvivalencija omogućuje modeliranje funkcionalne diferencijacije: nadređeni, podređeni, klijenti, dobavljači, brokeri.
Upravo je regularna ekvivalencija temelj blokmodeliranja i analize pozicija jer reducira kompleksnu mrežu na sustav odnosa među tipovima aktera, a ne među pojedincima.
U Enron mreži regularna ekvivalencija može identificirati:
sve menadžere koji šalju poruke podređenima i primaju ih od nadređenih,
sve administrativne asistente koji komuniciraju s voditeljima,
sve “broker” pozicije koje povezuju dva klastera.
Ovdje osobe nisu povezane s istim pojedincima, ali su povezane s akterima iste vrste. Regularna ekvivalencija tako otkriva organizacijske uloge: nadređeni, podređeni, posrednici, specijalisti.
To je već korak prema blokmodeliranju — reduciramo mrežu na odnose među tipovima pozicija.
Za regularnu ekvivalenciju koristimo redist iz
sna i opet klasteriramo.
regular_equiv_classes <- function(g, k = 8) {
A <- as.matrix(as_adj(g, sparse = FALSE)) # binarna out-adjacency
d <- sna::redist(A) # regularne udaljenosti :contentReference[oaicite:7]{index=7}
hc <- hclust(as.dist(d), method = "average")
cl <- cutree(hc, k = k)
split(V(g)$Name, cl)
}
re_classes_enron <- regular_equiv_classes(gE, k = 8)
sapply(re_classes_enron, length) |> sort(decreasing = TRUE)
## 1 4 3 2 5 6 7 8
## 160 8 7 2 2 1 1 1
Regularna ekvivalencija u Enron mreži hvata ono što strukturna ne može: akteri su regularno ekvivalentni ako imaju veze prema ekvivalentnim tipovima aktera, čak i kada to nisu isti pojedinci. U organizacijskom kontekstu to je upravo ona razina na kojoj počinju “izranjati” uloge: menadžerske razine, administrativna podrška, specijalisti koji komuniciraju prema više funkcionalnih jedinica te brokeri koji povezuju dijelove organizacije.
Dobiveni rezultat klasteriranja na temelju sna::redist()
u Enronu pokazuje izrazitu asimetriju veličina klasa (npr. jedna vrlo
velika klasa i nekoliko manjih). Takav ishod je čest u realnim
komunikacijskim mrežama kada se primjenjuje relativno mali broj klasa
k: algoritam često obuhavti veliki dio aktera u jednu široku
regularnu poziciju koja predstavlja dominantni tip komunikacijskog
ponašanja (npr. mnogi akteri koji primarno komuniciraju unutar svojih
lokalnih krugova bez vrlo specifičnog vertikalnog ili brokerskog
profila), dok se manjim klasama izdvajaju akteri s izraženijim i
stabilnijim obrascima (npr. čvorovi s jakim outflowom prema
mnogima, čvorovi koji primaju od mnogih, ili čvorovi koji vežu različite
dijelove mreže).
Interpretativno, regularna ekvivalencija u Enronu je najkorisnija kada se klase ne čitaju kao “odjeli”, nego kao funkcionalni tipovi komunikacije. Tipične uloge su npr. “snažni pošiljatelji prema više timova”, “centralne administrativne točke”, “primatelji mnogih poruka” i sl. Velika klasa tada predstavlja “uobičajenu” organizacijsku komunikaciju bez snažne diferencijacije u makro-ulozi, a male klase predstavljaju strukturno distinktne uloge (npr. koordinacijske točke, pozicije s izrazitom vertikalnom asimetrijom, ili pozicije koje su redovito u sredini putova). To je i razlog zašto se nakon toga prirodno prelazi na blokmodeliranje: cilj nije samo klasifikacija, nego razumijevanje odnosa među tim ulogama.
UNIPU mreža je gotovo “školski primjer” regularne ekvivalencije.
Svi nositelji su regularno ekvivalentni jer primaju usmjerene veze od izvođača.
Svi izvođači su regularno ekvivalentni jer imaju usmjeren odnos prema nositeljima.
“Vanjski suradnici” mogu tvoriti zasebnu regularnu poziciju jer su tipično povezani s internim nositeljima.
Osobe koje povezuju više sastavnica mogu tvoriti brokersku regularnu poziciju.
Ovdje se jasno pojavljuje pojam uloge: nositelj, izvođač, među-sastavnički most, centralni koordinator.
Regularna ekvivalencija zato daje najsmisleniju interpretaciju za UNIPU mrežu jer odgovara funkcionalnoj diferencijaciji sustava.
regular_equiv_classes <- function(g, k = 8) {
A <- as.matrix(as_adj(g, sparse = FALSE)) # binarna out-adjacency
d <- sna::redist(A) # regularne udaljenosti :contentReference[oaicite:7]{index=7}
hc <- hclust(as.dist(d), method = "average")
cl <- cutree(hc, k = k)
split(V(g)$name, cl)
}
re_classes_unipu <- regular_equiv_classes(gU, k = 8)
sapply(re_classes_unipu, length) |> sort(decreasing = TRUE)
## 4 2 5 1 6 3 7 8
## 119 91 55 25 25 23 3 3
U UNIPU mreži regularna ekvivalencija ima posebno jasnu semantiku jer je relacija konstruirana tako da uključuje i hijerarhijsku komponentu (npr. izvođač \(→\) nositelj). To znači da se “tipovi” aktera u mreži često mogu opisati kroz stabilne relacije prema drugim tipovima: izvođači imaju veze prema nositeljima, nositelji primaju od izvođača, a dio aktera može imati mješovite obrasce (npr. i nosi i izvodi, ili povezuje više timova/kolegija). Upravo takve situacije regularna ekvivalencija zahvaća prirodno, jer ne traži iste suradnike nego sličan odnos prema sličnim ulogama - uloge tipa “osobe koje su često nositelji i imaju mnogo izvođača” naspram “osobe koje većinom izvode (usmjereni tok prema nositeljima)” — čak i ako ne rade s istim ljudima.
Rezultat u kojem se dobiva nekoliko većih klasa i nekoliko malih (npr. dvije ili tri dominantne pozicije i par malih specijalnih pozicija) tipično znači da mreža doista sadrži nekoliko osnovnih funkcionalnih uloga koje se ponavljaju kroz sustav, uz manji broj aktera s rubnim ili kombiniranim profilima. U UNIPU interpretaciji, veće klase najčešće odgovaraju glavnim funkcionalnim tipovima (npr. pretežno izvođački profil, pretežno nositeljski profil, mješoviti profil), dok male klase često hvataju aktere koji rade na specifičan način (npr. povezuju više inače slabije povezanih dijelova, imaju izrazitu koncentraciju odnosa, ili se pojavljuju u neuobičajenim konfiguracijama kolegija).
U odnosu na Enron, regularna ekvivalencija je ovdje interpretativno “čišća” jer se strukturni tipovi lakše vežu uz značenje relacije. Zato se i makro-arhitektura (npr. blok gustoće između regularnih pozicija) često može čitati kao trag hijerarhijske logike: očekivano je da blokovi koji odgovaraju izvođač → nositelj budu izraženiji od obrnutog smjera, dok dijagonalni blokovi otkrivaju koliko su uloge unutar sebe kohezivne (npr. koliko se “nositelji” međusobno povezuju, ili koliko se “izvođači” povezuju izvan odnosa prema nositeljima).
| Koncept | Enron | UNIPU | Ideja |
|---|---|---|---|
| Strukturna | Rijetka, gotovo nikad savršena | Moguća u malim nastavnim timovima | radimo s istim ljudima |
| Automorfna | Lokalna simetrija u podstrukturama | Simetrije unutar kompetencijskih klastera | nalazimo se na istom mjestu u strukturi |
| Regularna | Hijerarhijske i brokerske uloge | Jasne funkcionalne uloge (nositelj, izvođač) | radimo istu vrstu posla u sustavu |
Tri oblika ekvivalencije mogu se promatrati kao kontinuum:
Kako se krećemo tim kontinuumom, opuštamo kriterij identičnosti i prelazimo s mikro-razine (konkretne veze) na mezorazinu (pozicije) i naposljetku na makrorazinu (uloge u sustavu).
Automorfna ekvivalencija u obje mreže prije svega govori o prisutnosti topoloških simetrija i zato je u realnim, heterogenim mrežama često ograničene analitičke vrijednosti izvan demonstracije koncepta; u Enronu se tipično očekuje vrlo malo netrivijalnih simetrija, dok u UNIPU mreži mogu postojati lokalno ponavljajući obrasci koji generiraju više automorfnih klasa. Regularna ekvivalencija je, nasuprot tome, primarni “most” prema ulogama i blokmodeliranju: u Enronu ona hvata funkcionalne tipove komunikacije (često s jednom velikom “baznom” klasom i nekoliko specifičnih), dok u UNIPU mreži prirodno mapira institucionalno smislenije uloge jer je relacija već konstruirana s ugrađenom funkcionalnom logikom.
U metodološkom smislu, taj prijelaz označava i promjenu istraživačkog pitanja:
Time se otvara prostor za blokmodeliranje, analizu hijerarhije i identifikaciju brokera, gatekeepera i autoriteta, jer se uloga ne može objasniti isključivo centralnošću, nego zahtijeva razumijevanje strukturne zamjenjivosti u relacijskom kontekstu.
Pozicijska analiza predstavlja metodološki prijelaz s analize pojedinačnih aktera na analizu strukturnih klasa unutar mreže. Dok mjere centralnosti rangiraju čvorove prema njihovoj važnosti, pozicijska analiza odgovara na pitanje: koje tipove aktera mreža generira? Njezin je cilj reducirati kompleksnu mrežu velikog broja čvorova i veza na manji broj pozicija, pri čemu se pozicija definira kao skup aktera koji su međusobno ekvivalentni prema određenom kriteriju (najčešće strukturne ili regularne ekvivalencije).
U formalnom smislu, pozicijska analiza polazi od matrice susjedstva \(X = [x_{ij}]\), gdje \(x_{ij}\) označava postojanje ili težinu veze od aktera \(i\) prema akteru \(j\). Ako je mreža veličine \(n\), tada je \(X\) dimenzija \(n \times n\). Cilj je pronaći particiju skupa aktera na \(K\) disjunktnih klasa (pozicija), pri čemu vrijedi:
\[ \bigcup_{k=1}^{K} P_k = V, \quad P_k \cap P_l = \varnothing \ (k \neq l), \]
gdje je \(V\) skup svih čvorova, a \(P_k\) skup aktera u poziciji \(k\). Drugim riječima, svaki akter pripada točno jednoj poziciji.
Prvi korak u pozicijskoj analizi jest mjerenje sličnosti (ili udaljenosti) između aktera na temelju njihovih obrazaca veza. U slučaju strukturne ekvivalencije uspoređuju se retci (i/ili stupci) matrice susjedstva. Npr., Euklidska udaljenost između aktera \(i\) i \(j\) definira se kao:
\[ d_{ij} = \sqrt{\sum_{k=1}^{n} (x_{ik} - x_{jk})^2}. \]
Ovdje \(x_{ik}\) i \(x_{jk}\) predstavljaju veze aktera \(i\) i \(j\) prema akteru \(k\). Što je \(d_{ij}\) manja, to su akteri sličniji u obrascu svojih veza.
Alternativno, može se koristiti Pearsonov koeficijent korelacije, koji mjeri linearnu povezanost između redaka matrice susjedstva. U praksi se na temelju dobivene matrice sličnosti primjenjuju algoritmi grupiranja, poput hijerarhijske klasterske analize (HCA) ili CONCOR algoritma (iterirane korelacije), kako bi se akteri svrstali u pozicije.
Nakon definiranja pozicija, prelazi se na blokmodeliranje. Blokmodeliranje je postupak u kojem se originalna matrica susjedstva reorganizira (permutira) tako da akteri iste pozicije stoje jedni uz druge. Time se u matrici formiraju blokovi — podmatrice koje predstavljaju odnose unutar i između pozicija.
Ako je mreža podijeljena na \(K\) pozicija, tada se matrica može prikazati kao blok-matrica dimenzija \(K \times K\), gdje svaki element predstavlja skup odnosa između dviju pozicija. Gustoća bloka između pozicija \(P_a\) i \(P_b\) računa se kao:
\[ \delta_{ab} = \frac{\sum_{i \in P_a} \sum_{j \in P_b} x_{ij}}{|P_a| \cdot |P_b|}. \]
Ovdje \(|P_a|\) i \(|P_b|\) označavaju broj aktera u odgovarajućim pozicijama, dok brojnik predstavlja ukupan broj ostvarenih veza između tih skupina. Gustoća \(\delta_{ab}\) interpretira se kao vjerojatnost postojanja veze između tipova aktera.
Kako bi se mreža dodatno pojednostavila, gustoće se često transformiraju u binarne vrijednosti (0 ili 1) na temelju određenog praga \(\alpha\). Ako vrijedi:
\[ \delta_{ab} > \alpha, \]
blok se označava kao 1 (postoji relacija između pozicija), inače kao 0. Rezultat je matrica slike — reducirani prikaz makrostrukture mreže u kojem su čvorovi same pozicije, a veze predstavljaju tipične obrasce odnosa među njima.
Ovaj postupak omogućuje prelazak s mikro-razine (pojedinačne veze) na makro-razinu (odnosi među klasama aktera). Drugim riječima, kompleksna mreža s velikim brojem čvorova transformira se u sažeti graf s malim brojem strukturnih tipova.
Proces pretvaranja sirovih mrežnih podataka u razumljiv blokmodel odvija se kroz nekoliko koraka:
Permutirana matrica susjedstva: Redovi i stupci originalne matrice se preslaguju (permutiraju) tako da akteri koji pripadaju istom bloku (klasteru) stoje jedni uz druge. Time blokovi postaju vizualno uočljivi kao kvadrati unutar matrice.
Matrica gustoće (Density matrix): Svaki blok u permutiranoj matrici zamjenjuje se jednom vrijednošću koja predstavlja gustoću veza (udio ostvarenih u odnosu na moguće veze) unutar tog bloka.
Matrica slike (Image matrix): Gustoće se pretvaraju u binarne vrijednosti (0 ili 1) na temelju određenog kriterija (praga):
Reducirana makrostruktura (Graf slike): Blokmodel se konačno prikazuje kao pojednostavljeni graf u kojem su čvorovi sami blokovi, a veze među njima predstavljaju relacije utvrđene matricom slike.
Ovisno o obrascu unutar blokova, razlikuju se različite makro-strukture:
Blokmodeliranje se može provoditi deduktivno (testiranje unaprijed definirane teorijske strukture) ili induktivno (otkrivanje strukture iz podataka). U oba slučaja, rezultat je formalna reprezentacija makro-arhitekture sustava.
Važno je naglasiti da blokmodeliranje uključuje niz odluka istraživača: izbor kriterija ekvivalencije, broj pozicija \(K\), prag \(\alpha\) i tip blokova. Te odluke izravno utječu na interpretaciju rezultata. Model je stoga uvijek pojednostavljenje stvarnosti, a ne njezina potpuna reprodukcija.
Unatoč tome, pozicijska analiza i blokmodeliranje omogućuju ono što pojedinačne mjere ne mogu: otkrivanje stabilnih uloga, strukturnih nejednakosti i funkcionalne diferencijacije u kompleksnim mrežama. Time se mreža prestaje promatrati kao skup izoliranih čvorova i postaje sustav odnosa među tipovima aktera, što predstavlja temelj za razumijevanje hijerarhije, moći i institucionalne organizacije.
Ovdje su nam pomoćne funkcije koje ćemo koristiti za raščlambu. Ove funkcije nam omogućuju da od grafa dođemo do pozicija (klastera po strukturnoj sličnosti) i zatim do blokmodela (gustoće među pozicijama) i na kraju do grafa slike (makrostrukture).
#Pomoćne funkcije
library(igraph)
# 0/1 matrica susjedstva
adj_mat <- function(g, weights = FALSE) {
if (weights && "weight" %in% edge_attr_names(g)) {
as.matrix(as_adjacency_matrix(g, attr = "weight", sparse = FALSE))
} else {
as.matrix(as_adjacency_matrix(g, sparse = FALSE))
}
}
Ova pomoćna funkcija pretvara graf g u matricu susjedstva (adjacency matrix):
ako je weights = FALSE, vraća 0/1
matricu:
A[i, j] = 1 ako postoji brid i → j,
inače 0.
ako je weights = TRUE i graf ima atribut brida
weight, vraća težinsku matricu:
A[i, j] = weight(i→j) (npr. broj mailova ili broj
zajedničkih kolegija po smjeru).
Vraća običnu (gustu) R matricu tipa matrix, dimenzije \(n × n\).
Skoro sve u blokmodeliranju kreće od matrice susjedstva:
strukturna sličnost se računa usporedbom redaka/stupaca te matrice
blok gustoće se računa kao udio (ili prosjek težina) unutar podmatrica \(A[ia, ib]\)
Drugim riječima: graf \(\rightarrow\) matrica \(\rightarrow\) udaljenosti \(\rightarrow\) pozicije \(\rightarrow\) blokovi \(\rightarrow\) slika.
# Strukturna sličnost: udaljenost po OUT + IN obrascu (spajamo redak i stupac)
# -> ovo je standardna praktična aproksimacija strukturne ekvivalencije
dist_structural <- function(g, weights = FALSE, method = "euclidean", scale = TRUE) {
A <- adj_mat(g, weights = weights)
X <- cbind(A, t(A)) # OUT + IN obrazac
if (scale) X <- scale(X)
dist(X, method = method)
}
Računa udaljenost između čvorova na temelju njihovog obrasca veza.
Uzme matricu A (0/1 ili težinsku)
Napravi matricu obilježja:
X <- cbind(A, t(A))
A predstavlja OUT obrazac (kome čvor šalje / na koga
“ide”)
t(A) predstavlja IN obrazac (tko čvor prima / tko
“dolazi” u čvor)
Dakle, za svaki čvor dobijemo jedan dugi vektor: [OUT veze | IN veze]
Standardizira stupce (scale(X)), da spriječi da
dugačke kolone dominiraju (npr. ako neke osobe imaju ekstremno puno
veza).
Izračuna dist(X, method) - euklidska
udaljenost.
Vraća objekt klase dist (standardni format udaljenosti u R-u).
Ovo je praktična operacionalizacija ideje:
strukturna ekvivalencija = identični OUT i IN obrasci
u praksi su rijetko identični, zato radimo strukturnu sličnost (tko je “najbliži” po obrascu veza), a za to nam treba matrica udaljenosti - ona je ulaz za klasteriranje (pozicije).
# Klasteriranje -> pozicije
positions_hclust <- function(g, K, weights = FALSE, method = "ward.D2") {
d <- dist_structural(g, weights = weights)
hc <- hclust(d, method = method)
cl <- cutree(hc, k = K)
list(hc = hc, pos = cl)
}
positions_hclust(g, K, weights = FALSE, method = "ward.D2")
dist_structural().Koraci:
izračuna d (udaljenosti)
napravi dendrogram:
hc <- hclust(d, method = method)
“reže” dendrogram na K grupa:
cl <- cutree(hc, k = K) (gdje mi zadajemo k)
Vraća listu:
hc = cijeli dendrogram (koristan za vizualizaciju)
pos = vektor duljine n, gdje pos[i] govori kojoj
poziciji pripada čvor i
To je korak pretvaranja sličnosti u pozicije. U pozicijskoj analizi to je ključ: ne zanima nas samo tko je centralan, nego koji su tipovi aktera (klase sličnih obrazaca veza).
# Matrica gustoća između pozicija (binarno ili težinski)
block_density <- function(g, pos, weights = FALSE) {
A <- adj_mat(g, weights = weights)
labs <- sort(unique(pos))
B <- matrix(0, nrow = length(labs), ncol = length(labs),
dimnames = list(paste0("P", labs), paste0("P", labs)))
for (a in labs) for (b in labs) {
ia <- which(pos == a)
ib <- which(pos == b)
# moguće veze (bez self-loopova ako a==b)
denom <- length(ia) * length(ib)
if (a == b) denom <- length(ia) * (length(ia) - 1)
if (denom <= 0) {
B[paste0("P", a), paste0("P", b)] <- NA
} else {
B[paste0("P", a), paste0("P", b)] <- sum(A[ia, ib]) / denom
}
}
B
}
block_density(g, pos, weights = FALSE)
računa matricu gustoća između pozicija (blok-matricu).
pos je vektor pozicija (npr. 1..K)
labs su sve pozicije koje postoje
B[a,b] računa prosječnu “gustoću” veza iz pozicije a
prema poziciji b
ia <- which(pos == a) (indeksi čvorova u poziciji
a)
ib <- which(pos == b) (indeksi čvorova u poziciji
b)
uzme podmatricu A[ia, ib] i zbroji sve veze
podijeli s brojem mogućih veza denom. Što je brojnik
ovdje:
u binarnoj mreži: brojnik = broj bridova između pozicija
u težinskoj mreži: brojnik = zbroj težina između pozicija.
Posebno: kad je a == b, izbacuje self-loop
mogućnosti: |ia| * (|ia|-1) umjesto
|ia|*|ia|
Vraća numeričku matricu B dimenzije K × K, s imenima redaka/stupaca “P1”, “P2”, itd. To je srce blokmodeliranja: umjesto da gledamo tisuće čvorova, gledamo:
kakav je odnos pozicija prema pozicijama
jesu li blokovi “kohezivni” (visoka dijagonala)
postoje li asimetrični tokovi (npr. P3 → P1 visoko, ali P1 → P3 nisko)
Napomena: ako je weights=TRUE,
sum(A[ia,ib]) se ponaša kao “ukupna težina” među
pozicijama, pa B postaje prosječna težina po mogućoj vezi.
# Matrica slike (0/1) prema pragu:
# - "mean": prag = prosjek nenan elemenata matrice gustoća
# - ili ručno alpha
image_matrix <- function(B, alpha = c("mean", "density"), global_density = NULL) {
if (is.character(alpha)) {
if (alpha[1] == "mean") {
thr <- mean(B, na.rm = TRUE)
} else if (alpha[1] == "density") {
thr <- global_density
} else stop("Nepoznat alpha.")
} else {
thr <- alpha
}
I <- ifelse(is.na(B), NA, ifelse(B > thr, 1, 0))
list(I = I, threshold = thr)
}
image_matrix(B, alpha = c("mean","density"), global_density = NULL)
pretvara matricu blok gustoća B u matricu slike I (0/1):
odredi prag thr:
“mean”: prag je prosjek vrijednosti u B (ignorira NA)
“density”: prag se zada izvana preko global_density
ili alpha može biti broj (npr. 0.05)
onda: I[a,b] = 1 ako je B[a,b] > thr, inače 0 (NA
ostaje NA)
Vraća listu:
I = matrica 0/1 (image matrix)
threshold = prag koji je korišten
Blok gustoće B je već redukcija, ali je i dalje “kontinuirana” i teža za brzo čitanje. Matrica slike daje najgrublji makro-prikaz:
postoji li “značajan” odnos među pozicijama (1)
ili nema (0)
To je ono što kasnije crtamo kao graf (pozicije kao čvorovi).
# Graf slike (pozicije kao čvorovi)
image_graph <- function(I) {
I2 <- I
I2[is.na(I2)] <- 0
graph_from_adjacency_matrix(I2, mode = "directed", diag = FALSE)
}
Pretvara matricu slike \(I\) u graf gdje su čvorovi pozicije.
zamijeni NA s 0 (da bi matrica bila
valjana)
graph_from_adjacency_matrix(I2, mode="directed", diag=FALSE)
Vraća igraph objekt (graf pozicija). Ovo je finalni
“graf slike”: najjednostavniji prikaz makrostrukture.
Umjesto originalne mreže dobijemo graf od recimo 6–10 pozicija, koji se može interpretirati kao:
“tko tipično komunicira s kim”
“postoji li hijerarhija / tok”
“jezgra-periferija” (ako jedna pozicija ima veze prema mnogima)
g_giant)strukturna sličnost \(→\) pozicije \(→\) blokmodel
Odabir broja pozicija K - za vizualni prikaz je praktično uzeti K = 6 do 10 (da graf slike ostane čitljiv). Primjer s K = 6:
gE <- g_giant
K_E <- 6
resE <- positions_hclust(gE, K = K_E, weights = FALSE) # binarno
posE <- resE$pos
table(posE)
## posE
## 1 2 3 4 5 6
## 86 35 24 16 16 5
V(gE)$pos <- posE
Ovdje grupiramo čvorove prema tome koliko su im slični obrasci slanja i primanja poruka (OUT i IN). Dobivene “pozicije” nisu “odjeli”, nego strukturne klase: npr. čvorovi koji komuniciraju sličnim skupovima drugih čvorova (ili “na sličan način”).
Permutirana matrica i blok gustoće
B_E <- block_density(gE, posE, weights = FALSE)
round(B_E, 3)
## P1 P2 P3 P4 P5 P6
## P1 0.048 0.052 0.029 0.009 0.029 0.098
## P2 0.072 0.322 0.048 0.029 0.059 0.194
## P3 0.042 0.064 0.513 0.125 0.146 0.667
## P4 0.023 0.027 0.102 0.800 0.031 0.088
## P5 0.039 0.089 0.167 0.023 0.608 0.088
## P6 0.367 0.606 0.708 0.175 0.188 0.950
B_E[a,b] je gustoća veza iz bloka pozicija Pa prema Pb.
Visoke vrijednosti na dijagonali (\(Pa→Pa\)) sugeriraju “kohezivne” pozicije;
visoke vrijednosti izvan dijagonale sugeriraju usmjerene tokove između
tipova aktera (npr. “broadcast” pozicija → “receiving” pozicija).
Napomena:
permutirana matrica = vizualni trik (redoslijed čvorova)
matrica gustoće = stvarna redukcija (\(K×K\))
ordE <- order(posE)
AE <- adj_mat(gE, weights = FALSE)
image(AE[ordE, ordE], main = "ENRON: permutirana matrica susjedstva (po pozicijama)")
Matrica slike i graf slike
Prag možemo uzeti kao prosjek blok gustoća (mean fit)
ili alternativno, medijan:
imgE <- image_matrix(B_E, alpha = "mean")
imgE$threshold
## [1] 0.211766
I_E <- imgE$I
gE_img <- image_graph(I_E)
gE_img
## IGRAPH 7f6abdb DN-- 6 4 --
## + attr: name (v/c)
## + edges from 7f6abdb (vertex names):
## [1] P3->P6 P6->P1 P6->P2 P6->P3
plot(gE_img, vertex.label = V(gE_img)$name,
main = "ENRON: graf slike (pozicije kao čvorovi)")
Ovo je sažetak makrostrukture: čvorovi su pozicije, a brid znači “iznadprosječna gustoća veza” između pozicija. Ako vidimo npr. da \(P2 \rightarrow P5\) postoji, a obratno ne, to je indikacija asimetrije u komunikacijskom toku između dviju strukturnih klasa.
Ovo je odličan trenutak pokažemo regularnu ekvivalenciju bez
REGE/CONCOR implementacija: pozicije ne nastaju iz
sličnosti redaka, nego iz uloge. Ovdje pristupamo deduktivnom
blokmodeliranju (regularna ideja), gdje tretiramo “uloge” kao pozicije.
Enron Note sadrži pozicije tipa Vice President, Director, Manager i sl.,
pa možemo napraviti role-blokmodel:
gE <- g_giant
note <- V(gE)$Note
note <- ifelse(is.na(note) | note == "NA", "Unknown", note)
is_mgmt <- grepl("Vice President|Director|Manager|CEO|President|Managing Director", note, ignore.case = TRUE)
roleE <- ifelse(is_mgmt, "Management", "Non-management")
table(roleE)
## roleE
## Management Non-management
## 76 106
V(gE)$role_pos <- factor(roleE, levels = c("Management","Non-management"))
pos_roleE <- as.integer(V(gE)$role_pos)
B_roleE <- block_density(gE, pos_roleE, weights = FALSE)
round(B_roleE, 3)
## P1 P2
## P1 0.162 0.070
## P2 0.087 0.074
img_roleE <- image_matrix(B_roleE, alpha = "mean")
g_roleE_img <- image_graph(img_roleE$I)
plot(g_roleE_img, vertex.label = levels(V(gE)$role_pos),
main = "ENRON: graf slike (pozicije = Management/Non-management)")
Ovo je “uloga” blokmodel: promatramo jesu li komunikacijski tokovi različiti između menadžmenta i ostalih.
Management → Non-management blok izraženiji od obrnutog, to nalikuje broadcast/koordinaciji.
obratno, to može nalikovati reporting/eskalaciji.
oba jaka, mreža je više “kolaborativna” nego strogo hijerarhijska.
g_weak_giant)strukturna sličnost → pozicije → blokmodel
UNIPU mreža ima drugačiju semantiku brida (suradnja + hijerarhija), pa je zanimljivo vidjeti hoće li pozicije “izvući” tu logiku.
gU <- g_weak_giant
K_U <- 8
resU <- positions_hclust(gU, K = K_U, weights = FALSE)
posU <- resU$pos
table(posU)
## posU
## 1 2 3 4 5 6 7 8
## 33 242 15 24 18 4 1 7
V(gU)$pos <- posU
Blok gustoće i matrica slike
B_U <- block_density(gU, posU, weights = FALSE)
round(B_U, 3)
## P1 P2 P3 P4 P5 P6 P7 P8
## P1 0.964 0.008 0.006 0.004 0.005 0.000 0.03 0.000
## P2 0.011 0.011 0.003 0.011 0.012 0.043 0.05 0.004
## P3 0.006 0.002 0.800 0.000 0.000 0.000 1.00 0.000
## P4 0.004 0.011 0.000 0.683 0.000 0.010 0.00 0.000
## P5 0.005 0.009 0.000 0.000 1.000 0.000 0.00 0.286
## P6 0.000 0.043 0.000 0.010 0.000 1.000 0.00 0.000
## P7 0.030 0.033 0.267 0.000 0.000 0.000 NA 0.000
## P8 0.000 0.003 0.000 0.000 0.143 0.000 0.00 1.000
ordU <- order(posU)
AU <- adj_mat(gU, weights = FALSE)
image(AU[ordU, ordU], main = "Unipu: permutirana matrica susjedstva (po pozicijama)")
masa na dijagonali (najviše): takve pozicije su “suradnički klasteri” (ljudi koji često rade zajedno na kolegijima).
izraženi jednosmjerni blokovi: to može reflektirati dio konstrukcije brida Izvođač \(→\) Nositelj, tj. hijerarhijsku komponentu.
“lokalni” blokovi, to se često poklapa s grupiranjem po stručnosti/kompetenciji (a ne nužno po sastavnicama).
imgU <- image_matrix(B_U, alpha = "mean")
imgU$threshold
## [1] 0.1191644
I_U <- imgU$I
gU_img <- image_graph(I_U)
plot(gU_img, vertex.label = V(gU_img)$name,
main = "UNIPU: graf slike (pozicije kao čvorovi)")
Sad pristupamo deduktivnom blokmodeliranju - deduktivno blokmodeliranje po operacionaliziranim ulogama (IN/OUT tip), gdje tretiramo “uloge” kao pozicije.
Ako nemamo direktan atribut “uloga osobe”, možemo napraviti jednostavnu, transparentnu operacionalizaciju:
više OUT nego IN (češće “izvođač \(→\) nositelj”)
više IN nego OUT (češće “prima” veze, tj. nositelj na više kolegija)
deg_in <- igraph::degree(gU, mode = "in")
deg_out <- igraph::degree(gU, mode = "out")
roleU <- ifelse(deg_out > deg_in, "više_out",
ifelse(deg_in > deg_out, "više_in", "balans"))
table(roleU)
## roleU
## balans više_in više_out
## 126 72 146
V(gU)$role_pos <- factor(roleU, levels = c("više_in","balans","više_out"))
pos_roleU <- as.integer(V(gU)$role_pos)
B_roleU <- block_density(gU, pos_roleU, weights = FALSE)
round(B_roleU, 3)
## P1 P2 P3
## P1 0.056 0.038 0.022
## P2 0.039 0.029 0.014
## P3 0.042 0.014 0.025
img_roleU <- image_matrix(B_roleU, alpha = "mean")
g_roleU_img <- image_graph(img_roleU$I)
plot(g_roleU_img, vertex.label = levels(V(gU)$role_pos),
main = "UNIPU: graf slike (pozicije = uloga po IN/OUT)")
Ovo nije “savršena” regularna ekvivalencija, ali je intuitivno jasna: dobivamo pozicije koje su definirane ponašanjem u usmjerenoj mreži (tko češće “ide prema” drugima). Ako graf slike pokaže npr. snažan blok više_out \(→\) više_in, to podržava interpretaciju hijerarhijskog toka (izvođači prema nositeljima).
Blokovi su korisni jer su omogućili prijelaz s metaforičkog opisa društva na preciznu provjeru pretpostavki o moći, statusu i društvenoj integraciji. Bez alata poput blokmodeliranja, identifikacija ovih “dubokih struktura” u masovnim skupovima podataka bila bi intuitivno nemoguća.
U prethodnoj lekciji model jezgre–periferije analiziran je kao globalno svojstvo mreže: gusto povezana jezgra okružena rjeđe povezanom periferijom. U ovom poglavlju nadovezujemo se na taj model, ali ga promatramo iz pozicijske perspektive. Jezgra–periferija više nije samo opis gustoće, nego postaje specifičan blokmodel, odnosno rezultat grupiranja aktera u dvije (ili više) strukturne pozicije.
U idealnom binarnom modelu jezgra–periferija matrica slike ima oblik:
\[ \begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix} \]
gdje prvi redak/stupac predstavlja jezgru, a drugi periferiju. Element 1 označava prisutnost tipične veze između pozicija, dok 0 označava njezin izostanak. Takva struktura implicira:
Ovdje \(\delta_{ab}\) označava gustoću bloka između pozicija \(a\) i \(b\), definiranu kao udio ostvarenih u odnosu na moguće veze između tih skupina. Time jezgra–periferija postaje pozicijski model, a ne samo vizualni dojam mreže.
Važno je razlikovati dva pristupa:
U prvom slučaju jezgra je implicitno definirana kao skup najcentralnijih aktera. U drugom slučaju jezgra je definirana obrascem veza, a ne pojedinačnom važnošću. Moguće je da akter nema najvišu centralnost, ali pripada jezgri jer je dio gusto povezanog bloka.
Time se pokazuje ključna razlika između centralnosti i pozicije: jezgra nije nužno skup “najvažnijih”, nego skup aktera koji dijele istu strukturnu ulogu u makro-arhitekturi.
Jezgra–periferija predstavlja jedan od najjednostavnijih oblika makro-arhitekture. Općenito, makro-arhitektura označava obrazac odnosa među pozicijama, a ne među pojedincima. U tom smislu, svaka mreža može se reducirati na manji broj strukturnih tipova:
Blokmodeliranje omogućuje formalno testiranje takvih obrazaca. Ako je mreža podijeljena na \(K\) pozicija, tada makro-arhitekturu predstavlja matrica slike dimenzija \(K \times K\), koja sažima cjelokupnu strukturu sustava.
Model jezgre–periferije često implicira latentnu hijerarhiju, ali te dvije strukture nisu identične. Jezgra može biti horizontalno organizirana (visoka međusobna povezanost bez jasne vertikalne dominacije), dok hijerarhija zahtijeva asimetriju i acikličnost odnosa. U usmjerenim mrežama, gustoća veza iz periferije prema jezgri može upućivati na prestiž ili autoritet jezgre, dok su veze iz jezgre prema periferiji indikator kontrole ili distribucije resursa.
Stoga jezgra–periferija može biti interpretirana kao:
ovisno o smjeru i prirodi veza.
U kontekstu rasta mreže, jezgra često nastaje nakon prelaska kritične gustoće (prijelaz faze). Pojava gigantske komponente omogućuje konsolidaciju najpovezanijih aktera u stabilnu jezgru. Time jezgra postaje rezultat emergentne makro-organizacije, a ne unaprijed zadane strukture. Ovime ćemo se detaljnije pozabaviti kasnije u tekstu.
Identifikacija jezgre ovisi o:
Različiti kriteriji mogu proizvesti različite interpretacije jezgre. Stoga jezgra–periferija nije ontološka činjenica, nego model strukturalne redukcije.
U kontekstu ove lekcije, model jezgre–periferije služi kao prijelaz prema potpunoj pozicijskoj analizi. On pokazuje kako se globalno svojstvo mreže može reinterpretirati kao sustav pozicija i blokova. Time se makro-arhitektura mreže prestaje promatrati kao agregat pojedinačnih centralnosti i postaje strukturirani odnos među tipovima aktera, što je temelj za razumijevanje uloga, hijerarhije i funkcionalne diferencijacije u složenim sustavima.
Ovdje možemo napraviti model jezgra–periferija kao 2-pozicijski
blokmodel za Enron (g_giant) i UNIPU
(g_weak_giant).
Ideja: uzmemo jednu transparentnu proceduru particioniranja (npr. k-core kao aproksimaciju jezgre), dobijemo dvije skupine (Core i Periphery), pa izračunamo blok-gustoće:
i usporedimo ih s idealom \(\begin{pmatrix}1&1\\1&0\end{pmatrix}\).
Pomoćne funkcije - “jedan blok” koda koji radi za obje mreže.
library(igraph)
# 1) Jezgra/periferija preko k-core (na undirected projekciji)
# - core_method = "kmax": jezgra = čvorovi s coreness == max(coreness)
# - core_method = "k_ge": jezgra = čvorovi s coreness >= k (ručno)
# - core_method = "top_prop": jezgra = top x% po coreness (npr. 10%)
core_partition_kcore <- function(g, core_method = c("top_prop","kmax","k_ge"),
top_prop = 0.10, k = NULL) {
core_method <- match.arg(core_method)
# k-core je definiran za neusmjereni graf -> koristimo collapse projekciju
gU <- as.undirected(g, mode = "collapse")
kc <- coreness(gU)
if (core_method == "kmax") {
core_idx <- which(kc == max(kc, na.rm = TRUE))
} else if (core_method == "k_ge") {
stopifnot(!is.null(k))
core_idx <- which(kc >= k)
} else { # top_prop
thr <- as.numeric(quantile(kc, probs = 1 - top_prop, na.rm = TRUE))
core_idx <- which(kc >= thr)
}
core_names <- V(g)$name[core_idx]
per_names <- setdiff(V(g)$name, core_names)
list(
kcore = kc,
core = core_names,
periphery = per_names
)
}
# 2) Blok-gustoće za Core/Periphery (usmjereno ili neusmjereno)
# gustoća = broj bridova između skupova / broj mogućih bridova
block_densities_cp <- function(g, core_names) {
is_dir <- igraph::is.directed(g)
C <- V(g)[name %in% core_names]
P <- V(g)[!name %in% core_names]
nC <- length(C); nP <- length(P)
# edge selectors (igraph)
eCC <- E(g)[.from(C) & .to(C)]
eCP <- E(g)[.from(C) & .to(P)]
ePC <- E(g)[.from(P) & .to(C)]
ePP <- E(g)[.from(P) & .to(P)]
mCC <- length(eCC); mCP <- length(eCP); mPC <- length(ePC); mPP <- length(ePP)
# denom: broj mogućih bridova (bez self-loopova)
if (is_dir) {
dCC <- nC * (nC - 1)
dPP <- nP * (nP - 1)
dCP <- nC * nP
dPC <- nP * nC
} else {
# u neusmjerenom: parovi bez redoslijeda
dCC <- nC * (nC - 1) / 2
dPP <- nP * (nP - 1) / 2
dCP <- nC * nP
dPC <- nP * nC # isto kao dCP, ali držimo matricu 2x2 radi čitljivosti
}
dens <- matrix(NA_real_, 2, 2, dimnames = list(c("Core","Periphery"),
c("Core","Periphery")))
dens["Core","Core"] <- ifelse(dCC > 0, mCC / dCC, NA)
dens["Core","Periphery"] <- ifelse(dCP > 0, mCP / dCP, NA)
dens["Periphery","Core"] <- ifelse(dPC > 0, mPC / dPC, NA)
dens["Periphery","Periphery"] <- ifelse(dPP > 0, mPP / dPP, NA)
list(
sizes = c(core = nC, periphery = nP),
edges = c(CC = mCC, CP = mCP, PC = mPC, PP = mPP),
density = dens
)
}
# 3) Matrica slike (ideal jezgra-periferija) prema pragu (mean ili ručno)
image_matrix_2 <- function(B, alpha = c("mean","value"), value = NULL) {
alpha <- match.arg(alpha)
thr <- if (alpha == "mean") mean(B, na.rm = TRUE) else value
I <- ifelse(is.na(B), NA, ifelse(B > thr, 1, 0))
list(I = I, threshold = thr)
}
# 4) Brzi plot: oboji jezgru i periferiju
plot_core_periphery <- function(g, core_names, main = "Core–Periphery") {
V(g)$cp <- ifelse(V(g)$name %in% core_names, "Core", "Periphery")
cols <- c(Core = "black", Periphery = "grey80")
plot(g,
vertex.color = cols[V(g)$cp],
vertex.size = 4,
vertex.label = NA,
edge.arrow.size = 0.2,
main = main)
}
Zašto je ovo “pozicijska” analiza? Jer jezgru definiramo kao poziciju (Core), a ne kao “top 10 po centralnosti” (ili sl.), i onda gledamo blok-gustoće između pozicija.
K-core je definiran za neusmjerene grafove (klasično), pa se ovdje koristi projekcija koja ignorira smjer kako bi se dobila robusnija “kohezivna” jezgra.
g_giant): jezgra–periferija kao 2-blok
modelParticija: jezgra = top 10% po k-core indeksu
gE <- g_giant
V(gE)$name <- V(gE)$Name
cpE <- core_partition_kcore(gE, core_method = "top_prop", top_prop = 0.10)
length(cpE$core); length(cpE$periphery)
## [1] 94
## [1] 53
# Blok-gustoće (na usmjerenoj mreži, jer je Enron usmjeren)
bdE <- block_densities_cp(gE, core_names = cpE$core)
bdE$sizes
## core periphery
## 118 64
round(bdE$density, 4)
## Core Periphery
## Core 0.1502 0.0581
## Periphery 0.0376 0.0531
Kako čitamo bdE$density?
Matrica slike (2×2) i usporedba s idealom
imgE <- image_matrix_2(bdE$density, alpha = "mean")
imgE$threshold
## [1] 0.07474093
imgE$I
## Core Periphery
## Core 1 0
## Periphery 0 0
U idealnom jezgra–periferija modelu očekujemo:
Ovdje imamo poklapanje samo u C-C = 1, ostalo su nule. Prag “mean” može biti pregrub, a core-periphery u praksi često nije čisti obrazac (pogotovo u komunikacijskim mrežama gdje periferija može imati međusobne veze). Može se činiti kao da model ne valja, iako je zapravo česta empirijska situacija.
Kod usmjerenih mreža posebno gledamo asimetriju:
Vizualna ilustracija
set.seed(1)
plot_core_periphery(gE, cpE$core, main = "ENRON: jezgra (crno) i periferija (sivo)")
U Enron mreži jezgra–periferija ovdje je operacionalizirana kao dvopozicijski blokmodel dobiven transparentnom procedurom particioniranja (k-core na neusmjerenoj “collapse” projekciji), pri čemu se jezgra definira kao pozicija, a ne kao skup “najcentralnijih”. Takva definicija je metodološki konzistentna s pozicijskom perspektivom: akteri ulaze u jezgru zato što dijele strukturnu karakteristiku “ugrađenosti” u gusti podgraf, a ne zato što su pojedinačno visoko rangirani po jednoj centralnosti.
Izračunate blok-gustoće za usmjereni graf pokazuju da je \(\delta_{CC}\) najveća (\(\approx 0.150\)), što je u skladu s interpretacijom jezgre kao kohezivnog dijela sustava: unutar jezgre postoji relativno visok udio realiziranih usmjerenih veza u odnosu na broj mogućih. Međutim, gustoće između jezgre i periferije, \(\delta_{CP}\) i \(\delta_{PC}\), ostaju umjerene, a \(\delta_{PP}\) nije “blizu nule”, nego je usporediva s vezama između pozicija. To sugerira da, pod ovom particijom, periferija u Enronu nije čisto “rijetka zona” s minimalnom internom povezanošću, nego dio mreže u kojem i dalje postoji netrivijalna razina komunikacijskih relacija.
Asimetrija između \(\delta_{CP}\) i \(\delta_{PC}\) (ovdje je Core→Periphery veće od Periphery→Core) upućuje na to da jezgra u prosjeku više “emitira” veze prema periferiji nego obratno. U komunikacijskom kontekstu to je kompatibilno s interpretacijom jezgre kao koordinacijskog ili distribucijskog sloja (npr. slanje informacija, zadataka ili obavijesti prema širem krugu), iako takva interpretacija uvijek ostaje uvjetovana definicijom veze (što točno brid predstavlja u agregaciji e-maila).
Matrica slike dobivena pragom temeljenim na aritmetičkoj sredini gustoća rezultira obrascem u kojem samo blok jezgra→jezgra prelazi prag, a ostali blokovi padaju ispod njega. To ne znači da “jezgra–periferija ne postoji”, nego da je taj način binarizacije (jedan globalni prag na temelju sredine) vrlo strog i često reducira 2×2 matricu na “samo CC je dovoljno visok”. U praksi je za jezgra–periferija modele često smislenije interpretirati relativne odnose među gustoćama (redoslijed \(\delta_{CC}\) naspram ostalih) ili koristiti prag koji je teorijski motiviran (npr. očekivanje “PP nisko” u apsolutnom smislu), a ne nužno prag izračunat kao prosjek četiriju blokova.
g_weak_giant): jezgra–periferija u mreži
suradnje/ulogaUNIPU bridi su mješavina suradničke (N–N, I–I) i usmjerene uloge (I→N). Zbog toga je korisno gledati:
Particija: jezgra = top 10% po k-core indeksu
gU <- g_weak_giant
cpU <- core_partition_kcore(gU, core_method = "top_prop", top_prop = 0.10)
length(cpU$core); length(cpU$periphery)
## [1] 52
## [1] 292
bdU <- block_densities_cp(gU, core_names = cpU$core)
bdU$sizes
## core periphery
## 52 292
round(bdU$density, 4)
## Core Periphery
## Core 0.5128 0.0088
## Periphery 0.0112 0.0175
Kad je \(\delta_{CC}\) osjetno veća od ostalih blokova, jezgru možemo tumačiti kao kohezivnu strukturnu poziciju. Kad je \(\delta_{PP}\) niska, periferija je slabo međusobno povezana, što je konzistentno s jezgra–periferija modelom. U usmjerenoj mreži, razlika između \(\delta_{CP}\) i \(\delta_{PC}\) sugerira asimetrične tokove (npr. koordinacija nasuprot eskalaciji / prestižu), no ovdje to ne vidimo. Kod UNIPU mreže, jezgra se može tumačiti kao skup aktera s ponavljanim zajedničkim angažmanima, dok asimetrija može reflektirati dio definicije brida (izvođač → nositelj).
Matrica slike
imgU <- image_matrix_2(bdU$density, alpha = "mean")
imgU$threshold
## [1] 0.1375765
imgU$I
## Core Periphery
## Core 1 0
## Periphery 0 0
Ne vidimo obrazac blizak idealu (PP ~ 0, CC visoko), onda je “jezgra–periferija” nije dobar sažetak makro-arhitekture.
set.seed(1)
plot_core_periphery(gU, cpU$core, main = "UNIPU: jezgra (crno) i periferija (sivo)")
Jedna usporedba: jezgra kao pozicija vs “top centralnost” - kratka demonstracija razlike jezgre temeljem centralnosti i pozicijske jezgre
top_degree <- function(g, p = 0.10) {
d <- igraph::degree(g, mode = "all")
thr <- as.numeric(quantile(d, probs = 1 - p, na.rm = TRUE))
V(g)$name[d >= thr]
}
coreE_kcore <- cpE$core
coreE_deg <- top_degree(gE, p = 0.10)
length(intersect(coreE_kcore, coreE_deg)) / length(union(coreE_kcore, coreE_deg))
## [1] 0.2638889
Ovo daje Jaccard preklapanje: “jezgra kao pozicija nije nužno skup najcentralnijih čvorova”.
U UNIPU mreži ista procedura (k-core na neusmjerenoj projekciji, zatim blok-gustoće na izvornom grafu) daje izrazito jasnu separaciju u odnosu na Enron: \(\delta_{CC}\) je vrlo visoka \((\approx 0.513)\), dok su \(\delta_{CP}\), \(\delta_{PC}\) i \(\delta_{PP}\) znatno niže (reda veličine stotinki). Takav rezultat je mnogo bliži intuitivnom jezgra–periferija obrascu jer implicira da jezgra čini gust, kohezivan “klub” aktera među kojima su veze česte, dok periferija ima relativno malo veza i prema jezgri i unutar sebe.
Niska \(\delta_{PP}\) dodatno podržava interpretaciju periferije kao rjeđe povezane zone, a niske gustoće između pozicija upućuju na to da jezgra nije samo “centralni dio” koji je snažno povezan sa svim ostalima, nego prije svega interno kohezivna pozicija. To je interpretativno važno: jezgra–periferija u idealnom smislu očekuje i relativno snažnu povezanost Core–Periphery, dok ovdje dominantno iskače unutarjezgrena kohezija. U tom smislu, ovaj nalaz može sugerirati da UNIPU mreža, pod ovom particijom, više nalikuje sustavu s jednim vrlo gustim podskupom i većom “okolinom” koja nije intenzivno integrirana s jezgrom, nego je prisutna kroz slabije i rjeđe relacije.
S obzirom na to da UNIPU relacija može kombinirati suradničke veze i usmjerene uloge, interpretacija razlika \(\delta_{CP}\) i \(\delta_{PC}\) mora biti oprezna: asimetrija blokova može reflektirati institucionalni smjer relacije (npr. izvođač → nositelj) jednako kao i stvarne tokove koordinacije ili prestiža. Ipak, činjenica da su oba međubloka niska, a CC vrlo visok, najčvršće podupire zaključak o postojanju “pozicijske jezgre” kao kohezivnog bloka.
Matrica slike dobivena pragom sredine ponovno daje samo \(CC=1\), ali ovdje taj ishod ima jasniju semantiku: zato što je \(\delta_{CC}\) višestruko veća od ostalih, globalni prag prirodno odvaja jezgru od svega ostalog. U UNIPU slučaju to je konzistentno s time da jezgra dominira kao strukturni fenomen, dok ostali blokovi ostaju na nižoj” razini gustoće.
U Enronu dvopozicijski jezgra–periferija model, izveden k-core aproksimacijom, daje jezgru koja jest kohezivnija od ostatka sustava, ali periferija nije dovoljno “rijetka” da bi idealni obrazac bio dobar sažetak makro-arhitekture; jezgra se ovdje može čitati više kao “relativno gušća zona” unutar općenito gusto povezane komunikacijske mreže. U UNIPU mreži, naprotiv, kontrast \(\delta_{CC}\)) prema ostalim blokovima je toliko izražen da jezgra–periferija kao pozicijski blokmodel postaje znatno uvjerljivija redukcija, pri čemu je dominantna karakteristika jezgre visoka unutarnja kohezija, a ne nužno snažna integracija periferije kroz veze s jezgrom.
U prethodnim lekcijama hijerarhija je spominjana kao globalno svojstvo usmjerenih mreža, osobito u kontekstu asimetrije i acikličnosti. U ovom poglavlju promatramo hijerarhiju iz pozicijske perspektive: kao specifičnu makro-arhitekturu koja proizlazi iz diferencijacije uloga i vertikalne raspodjele moći. Drugim riječima, hijerarhija je strukturirani odnos među pozicijama, a ne samo skup centralnih aktera.
U graf-teorijskom smislu, čista hijerarhija modelira se kao usmjereni aciklički graf (Directed Acyclic Graph – DAG). Usmjerenost znači da su veze asimetrične (npr. nadređeni → podređeni), dok acikličnost znači da ne postoji zatvoreni usmjereni put koji bi omogućio povratak na početni čvor.
Formalno, u DAG-u ne postoji niz čvorova \(v_1, v_2, \dots, v_k\) takav da:
\[ v_1 \rightarrow v_2 \rightarrow \dots \rightarrow v_k \rightarrow v_1. \]
Odsutnost ciklusa jamči jednoznačnu orijentaciju moći ili autoriteta. Ako bi ciklus postojao, to bi impliciralo uzajamnu dominaciju, što je u suprotnosti s idejom hijerarhije.
Najčišći oblik hijerarhije jest usmjereno stablo s jedinstvenim korijenom (outtree). U takvoj strukturi postoji jedan vršni čvor (korijen) iz kojeg polaze svi ostali putevi, a svaki čvor (osim korijena) ima točno jednog nadređenog. Ako mreža ima \(n\) čvorova, broj bridova u outtree strukturi iznosi:
\[ m = n - 1. \]
Ova relacija označava minimalan broj veza potreban da bi svi čvorovi ostali povezani bez stvaranja ciklusa. Outtree je stoga najefikasniji oblik hijerarhijske organizacije jer koristi najmanji broj veza za održavanje potpune dostižnosti.
U empirijskim mrežama rijetko nalazimo savršeni outtree, ali on služi kao normativni model prema kojem se procjenjuje stupanj hijerarhijskosti sustava.
David Krackhardt predložio je skup graf-teorijskih indeksa za kvantifikaciju stupnja do kojeg mreža nalikuje idealnoj hijerarhiji (Krackhardt, 2014). Ove mjere operacionaliziraju četiri dimenzije:
Connectedness (Povezanost) – udio parova čvorova koji su međusobno dostižni putem nekog usmjerenog puta. U savršenoj hijerarhiji vrijednost je 1.
Hierarchy (Hijerarhija) – mjera odsutnosti reciprociteta. Ako postoji veza \(i \rightarrow j\), tada ne smije postojati \(j \rightarrow i\). Indeks poprima vrijednost 1 kada nema recipročnih odnosa.
Efficiency (Efikasnost) – odnos stvarnog broja veza prema minimalnom broju potrebnom za održavanje povezanosti (\(n - 1\)). Svaka dodatna veza koja stvara alternativne putove smanjuje efikasnost.
Least Upper Boundness (LUBness) – mjeri postoji li jedinstveni vrh sustava, odnosno ima li svaki par čvorova zajedničkog nadređenog koji ih povezuje.
Sve četiri mjere skalirane su u intervalu \([0,1]\), gdje vrijednost 1 označava potpunu usklađenost s idealnim hijerarhijskim modelom.
Iz perspektive pozicijske analize, hijerarhija se može interpretirati kao specifičan blokmodel u kojem su blokovi organizirani vertikalno. Matrica slike poprima gornjetrokutasti (ili donjetrokutasti) oblik, što odražava asimetrične relacije među pozicijama.
Ako je mreža podijeljena na \(K\) pozicija, hijerarhijska struktura podrazumijeva da za svaki par pozicija \((P_a, P_b)\) vrijedi:
\[ \delta_{ab} > 0 \Rightarrow \delta_{ba} \approx 0, \]
pri čemu \(\delta_{ab}\) označava gustoću veza iz pozicije \(a\) prema poziciji \(b\). Time se formalizira jednosmjernost dominacije između razina.
Hijerarhija stoga nije samo raspodjela centralnosti, nego uređeni sustav pozicija u kojem su uloge diferencirane prema vertikalnom principu (npr. uprava \(→\) menadžment \(→\) operativna razina).
Važno je razlikovati formalno propisanu hijerarhiju (organizacijska shema) od hijerarhije koja proizlazi iz stvarnih interakcija (npr. mreža savjeta ili utjecaja). Analiza mreže često otkriva odstupanja između te dvije razine. Neformalni lideri mogu imati veću strukturnu moć od formalno nadređenih, osobito ako zauzimaju brokerske ili centralne pozicije.
Time hijerarhija postaje empirijski mjerljiva struktura, a ne samo normativna kategorija.
Hijerarhijske mreže imaju specifične funkcionalne karakteristike:
Uklanjanje korijena u outtree strukturi može fragmentirati cijeli sustav. Suprotno tome, horizontalno organizirane mreže pokazuju veću otpornost, ali manju koordinacijsku jasnoću.
U kontekstu ove lekcije, hijerarhija predstavlja završni oblik pozicijske diferencijacije: mreža više nije samo skup ekvivalentnih klasa, nego uređeni sustav razina. Time se koncept pozicije proširuje iz horizontalne sličnosti (ekvivalencija) na vertikalnu diferencijaciju (dominacija i podređenost), što je temelj za razumijevanje moći, autoriteta i institucionalne organizacije u društvenim i tehničkim sustavima.
# 1) Je li mreža DAG?
igraph::is_dag(gE)
## [1] FALSE
igraph::is_dag(gU)
## [1] FALSE
# 2) Koliko je mreža recipročna (0..1); čista hijerarhija -> blizu 0
igraph::reciprocity(gE, ignore.loops = TRUE)
## [1] 0.6066445
igraph::reciprocity(gU, ignore.loops = TRUE)
## [1] 0.9218551
# 3) Jaka povezanost (SCC): ciklusi se pojavljuju kao SCC veličine > 1
sccE <- igraph::components(gE, mode = "strong")
sccU <- igraph::components(gU, mode = "strong")
sort(sccE$csize, decreasing = TRUE)[1:10]
## [1] 174 1 1 1 1 1 1 1 1 NA
sort(sccU$csize, decreasing = TRUE)[1:10]
## [1] 296 6 3 3 2 2 2 2 1 1
# Udio čvorova koji su u "cikličkim grupama" (SCC > 1)
mean(sccE$csize[sccE$membership] > 1)
## [1] 0.956044
mean(sccU$csize[sccU$membership] > 1)
## [1] 0.9186047
“Kompromis” za empiriju: kondenzacija u DAG (condensation graph)
is_dag(FALSE) za obje mreže samo kaže: postoji barem
jedan ciklus. U e-mail i suradničkim mrežama to je normalno.
reciprocity(gE)=0.6066 i
reciprocity(gU)=0.9219 govore “koliko često veze postoje u
oba smjera” te visok reciprocitet znači uzajamnu komunikaciju/suradnju,
što više očekujemo naći u UNIPU (suradničke veze, timovi) nego u Enronu
(iako i u e-mailu reciprocitet može biti visok zbog odgovaranja).
Čak i kad originalna mreža nije DAG, možemo je svesti na DAG tako da svaki SCC skupimo u jedan čvor. To je standardna tehnika: “hijerarhija među cikličkim blokovima”.
# Kondenzacija: svaki SCC postaje jedan "super-čvor" -> rezultat je DAG
# 1) Jake komponente
sccE <- igraph::components(gE, "strong")
# 2) Kontrakcija: svaka komponenta postaje jedan čvor
gE_cond <- igraph::contract(gE, sccE$membership)
# 3) Uklanjanje višestrukih bridova i self-loopova
gE_cond <- igraph::simplify(gE_cond, remove.loops = TRUE)
# 4) Provjera: sada mora biti DAG
is_dag(gE_cond)
## [1] TRUE
sccU <- igraph::components(gU, "strong")
gU_cond <- igraph::contract(gU, sccU$membership)
gU_cond <- igraph::simplify(gU_cond, remove.loops = TRUE)
is_dag(gU_cond)
## [1] TRUE
vcount(gE_cond)
## [1] 9
ecount(gE_cond)
## [1] 8
vcount(gU_cond)
## [1] 36
ecount(gU_cond)
## [1] 36
# Koliko je "razina" u toj DAG makro-strukturi?
# topo_sort daje poredak kompatibilan s hijerarhijom (ako je DAG)
topoE <- topo_sort(gE_cond, mode = "out")
topoU <- topo_sort(gU_cond, mode = "out")
head(names(topoE))
## [1] "NA"
## [2] "c(\"Albert Meyers\", \"Thomas Martin\", \"Andrea Ring\", \"Andrew Lewis\", \"Andy Zipper\", \"Jeffrey Shankman\", \"Barry Tycholiz\", \"Benjamin Rogers\", \"Bill Rapp\", \"NA\", \"Bradley Mckay\", \"NA\", \"Richard Sanders\", \"Cara Semperger\", \"Daron Giron\", \"Charles Weldon\", \"Chris Dorland\", \"Chris Germany\", \"NA\", \"Cooper Richey\", \"Craig Dean\", \"Dana Davis\", \"Dan Hyvl\", \"Danny McCarty\", \"Daren Farmer\", \"Darrell Schoolcraft\", \"Daron Giron\", \"David Delainey\", \"Susan Bailey\", \"NA\", \"Diana Scholtes\", \"Thomas Martin\", \"Don Baughman\", \n\"Drew Fossum\", \"James Steffes\", \"NA\", \"NA\", \"Mark Haedicke\", \"Elizabeth Sager\", \"Eric Bass\", \"Eric Saibi\", \"Errol McLaughlin\", \"Sandra Brawner\", \"Larry Campbell\", \"Peter Keavey\", \"Fletcher Sturm\", \"Frank Ermis\", \"Geir Solberg\", \"Geoffery Storey\", \"Gerald Nemec\", \"Greg Whalley\", \"Harpreet Arora\", \"Andrew Lewis\", \"Holden Salisbury\", \"Hunter Shively\", \"James Derrick\", \"James Steffes\", \"Jane Tholt\", \"NA\", \"Jason Wolfe\", \"Jay Reitmeyer\", \"Jeff Dasovich\", \"Jeff King\", \"John Hodge\", \"Jeffrey Shankman\", \n\"Jeffery Skilling\", \"Daren Farmer\", \"NA\", \"Jim Schwieger\", \"Vince Kaminski\", \"Steven Kean\", \"NA\", \"Joe Parks\", \"Joe Quenet\", \"Joe Stepenovitch\", \"John Arnold\", \"John Forney\", \"NA\", \"John Hodge\", \"John Lavorato\", \"John Zufferli\", \"Jonathan Mckay\", \"Fletcher Sturm\", \"Juan Hernandez\", \"Judy Townsend\", \"Philip Allen\", \"Kam Keiser\", \"Kate Symes\", \"Kay Mann\", \"Keith Holst\", \"Kenneth Lay\", \"Kevin Hyatt\", \"Kevin Presto\", \"Kevin Ruscitti\", \"Kimberly Watson\", \"Kim Ward\", \"Larry Campbell\", \"Lawrence May\", \"Randall Gay\", \n\"Lindy Donoho\", \"NA\", \"NA\", \"Patrice Mims\", \"Louise Kitchen\", \"Lynn Blair\", \"NA\", \"Marie Heard\", \"Mark Haedicke\", \"NA\", \"Mark Taylor\", \"Mark Whitt\", \"Martin Cuilla\", \"Matthew Lenhart\", \"Matthew Motley\", \"NA\", \"John Forney\", \"Michelle Cash\", \"Michelle Lokay\", \"Mike Carson\", \"Michael Grigsby\", \"Michael Maggi\", \"NA\", \"Mike Swerzbin\", \"Phillip Love\", \"Monika Causholli\", \"NA\", \"Kevin Presto\", \"Susan Scott\", \"Jane Tholt\", \"Patrice Mims\", \"Paul Thomas\", \"Peter Keavey\", \"Philip Allen\", \"Phillip Love\", \"Phillip Platter\", \n\"Randall Gay\", \"Richard Ring\", \"Richard Sanders\", \"Richard Shapiro\", \"Rick Buy\", \"Robert Badeer\", \"Robert Benson\", \"Rod Hayslett\", \"Ryan Slinger\", \"Sally Beck\", \"Sandra Brawner\", \"NA\", \"NA\", \"Scott Neal\", \"Shelley Corman\", \"Hunter Shively\", \"Stacy Dickson\", \"Stanley Horton\", \"Stephanie Panus\", \"Steven Kean\", \"Susan Bailey\", \"Susan Pereira\", \"Susan Scott\", \"Kim Ward\", \"Tana Jones\", \"Teb Lokey\", \"Theresa Staab\", \"John Hodge\", \"Thomas Martin\", \"Paul Lucci\", \"Tom Donohoe\", \"Tori Kuykendall\", \"Tracy Geaccone\", \n\"Vince Kaminski\", \"Vladi Pimenov\", \"Charles Weldon\", \"David Delainey\", \"Susan Pereira\", \"Stacey White\")"
## [3] "NA"
## [4] "NA"
## [5] "NA"
## [6] "Michelle Lokay"
head(names(topoU))
## [1] "231" "352" "056" "088" "212" "239"
Za Enron se vidi ogromna jaka komponenta (174) i vrlo visok udio čvorova u cikličkim skupinama. Treba naglasiti da to znači da je u agregiranoj mreži većina čvorova međusobno dostižna u oba smjera (postoji put i i→j i j→i), što je “anti-DAG” obrazac i tipično za komunikaciju.
Za UNIPU: SCC od 296 + nekoliko manjih SCC;
opet znači da većina čvorova pripada uzajamno povezanoj jezgri u smislu
dostižnosti (ne nužno core-periphery).
Kondenzacija (contract SCC u DAG) - vcount(gE_cond)=9 i
vcount(gU_cond)=36 su “super-čvorovi” koji predstavljaju
SCC blokove; DAG se odnosi na odnose među blokovima, ne
među pojedincima.
Provjera da Enron mreža nije DAG potvrđuje očekivani empirijski obrazac: e-mail komunikacija u organizaciji uključuje povratne petlje, uzajamno odgovaranje i kruženje informacija, pa stoga struktura ne može biti strogo aciklična. To je izravno vidljivo i u vrlo visokoj recipročnosti (\(≈0.61\)), koja implicira da velik udio parova čvorova razmjenjuje veze u oba smjera. U Krackhardtovu smislu, takva recipročnost smanjuje “čistoću” hijerarhije jer idealna hijerarhija pretpostavlja dominaciju bez uzvraćanja, odnosno usmjerenost nalik zapovjednom lancu.
Analiza jakih komponenti dodatno pojašnjava ovu sliku: najveća SCC u Enronu ima 174 čvora, a udio čvorova koji pripadaju komponentama većim od 1 iznosi \(≈0.96\). To znači da je gotovo cijeli sustav uklopljen u jednu veliku cikličku jezgru u smislu dostižnosti putem usmjerenih putova, što je kompatibilno s organizacijskom komunikacijom gdje se informacije i zahtjevi često mogu “vratiti” do polazišta kroz neki lanac posrednika. Kondenzacijom SCC-ova dobiva se DAG s vrlo malo “super-čvorova” (9) i gotovo linearnom strukturom (8 bridova), što sugerira da se većina cikličnosti događa unutar jedne dominantne komunikacijske mase, dok je hijerarhija među tim masama relativno gruba i niskodimenzionalna.
UNIPU mreža također nije DAG, što je očekivano s obzirom na to da sustav sadrži i simetrične relacije (N–N, I–I) i relacije koje u pravilu nose smjer (I→N), pa se ciklusi mogu pojaviti i kroz uzajamne suradnje i kroz kombinacije uloga. Recipročnost je vrlo visoka (\(≈0.92\)), što znači da je gotovo sva relacijska struktura uzajamna. To snažno ograničava interpretaciju mreže kao “hijerarhijske” u strogo graf-teorijskom smislu: čak i kad postoji vertikalna semantika u nekim bridovima, ukupna mrežna topologija dominatno je obilježena uzvraćanjem i kooperacijom.
Struktura jakih komponenti pokazuje veliku dominantnu SCC (296 čvorova) i nekoliko manjih cikličkih skupina, a udio čvorova u SCC-ovima većim od 1 je \(≈0.92\). To znači da je velika većina sustava uklopljena u ciklički povezanu jezgru, slično kao i kod Enrona, ali uz nešto više fragmentacije na dodatne SCC-ove. Kondenzacija daje znatno veći DAG nego u Enronu (36 super-čvorova i 36 bridova), što implicira da se cikličnost pojavljuje u više odvojenih “blokova” i da je makro-struktura među blokovima razgranatija. U institucionalnim podacima to je kompatibilno s postojanjem više relativno autonomnih nastavnih/organizacijskih klastera koji su interno gusto recipročni, ali su međusobno povezani preko ograničenog broja relacija.
Krackhardtovi indeksi hijerarhije
Najjednostavnije je koristiti paket sna jer on već ima Krackhardtove mjere (računaju se na matrici, ne na igraph objektu).
Važna napomena: sna funkcije očekuju matricu ili network
objekt. Ako im damo igraph, dobijemo grešku.
AE <- as.matrix(as_adjacency_matrix(gE, sparse = FALSE)) # 0/1
AU <- as.matrix(as_adjacency_matrix(gU, sparse = FALSE)) # 0/1
library(sna)
# Connectedness: koliko je parova čvorova dostižno putem usmjerenog puta
sna::connectedness(AE)
## [1] 1
sna::connectedness(AU)
## [1] 1
# Hierarchy: odsutnost recipročnosti (u Krackhardt smislu)
sna::hierarchy(AE)
## Mut
## 0.07188392
sna::hierarchy(AU)
## Mut
## 0.004169774
# Efficiency: “mršavost” strukture u odnosu na minimalni broj veza (n-1)
sna::efficiency(AE)
## [1] 0.9136473
sna::efficiency(AU)
## [1] 0.9761579
# LUBness: postoji li jedinstveni “gornji nadređeni” za parove
sna::lubness(AE)
## [1] 1
sna::lubness(AU)
## [1] 0.1749612
Kako to tumačimo (u tekstu):
Ove četiri vrijednosti možemo čitati kao “koliko mreža nalikuje idealnoj hijerarhiji”.
Za Enron tipično očekujemo: nižu hijerarhiju i nižu efikasnost (komunikacija je redundantna).
Za UNIPU očekujemo: hijerarhija nije “čista” (jer imamo simetrične veze N–N i I–I), ali u smjeru I→N često se vidi vertikalni element.
Krackhardt “hierarchy” je visok kad nema recipročnosti; ovdje je nizak jer je reciprocitet visok — to je konzistentno s prethodnom mjerom reciprociteta.
Za Enron, connectedness = 1 pokazuje da je, u smislu
usmjerenih putova, mreža maksimalno “dostižna” (što je u praksi česta
posljedica postojanja velike SCC). Hijerarhija je niska (\(≈0.072\)), što je konzistentno s visokom
recipročnosti i prisutnošću ciklusa: mreža je daleko od idealno
hijerarhijske. Istovremeno, visoka efikasnost (\(≈0.914\)) upućuje da je struktura relativno
“mršava” u odnosu na minimalni broj veza potreban za povezivost, ali bez
implikacije da je pritom hijerarhijska; efikasnost ovdje prije govori da
se dosezanje u mreži ostvaruje bez ekstremne redundancije, iako se i
dalje zadržavaju povratne veze. LUBness = 1 znači da, prema
toj mjeri, parovi čvorova imaju jedinstvene “najniže zajedničke
nadređene” u relevantnom smislu mjere, što se može javiti i u sustavima
koji nisu čisti DAG jer Lubness nije ekvivalentna zahtjevu
acikličnosti, nego stabilnosti nadređenih relacija u dosežnosti.
Za Unipu, connectedness = 1 opet pokazuje maksimalnu dostižnost, no hijerarhija je praktički nula (\(≈0.004\)), što je u skladu s recipročnosti bliskoj 1: sustav topološki nimalo ne nalikuje čistoj hijerarhiji. Efikasnost je vrlo visoka (\(≈0.976\)), što govori da se dosezanje realizira s relativno malo “viška” veza u odnosu na minimum potreban za povezivost, ali taj nalaz treba čitati uz oprez jer u visoko recipročnim mrežama redundancija ne mora izgledati kao “višak” u istom smislu kao u strogo hijerarhijskim strukturama. Lubness je niska (\(≈0.175\)), što sugerira da ne postoji stabilan jedinstveni “gornji” čvor za mnoge parove, što je upravo ono što očekujemo u sustavu s mnogo paralelnih suradničkih putova i bez jednoznačno definiranog vrha.
Hijerarhija kao blokmodel (pozicijska perspektiva)
Ovdje ne uvodimo nove funkcije — koristimo ono što već imamo iz blokmodel dijela:
posE, posU (pozicije iz HCA)
B_E, B_U (blok gustoće)
I_E, I_U (matrica slike)
Ideja: ako je hijerarhija prisutna između pozicija, onda se nakon dobrog poretka pozicija veze koncentriraju iznad (ili ispod) dijagonale.
netflow_E <- rowSums(B_E, na.rm = TRUE) - colSums(B_E, na.rm = TRUE)
netflow_U <- rowSums(B_U, na.rm = TRUE) - colSums(B_U, na.rm = TRUE)
ordE <- order(netflow_E, decreasing = TRUE)
ordU <- order(netflow_U, decreasing = TRUE)
# Permutirani blokovi (da “hijerarhija” postane vidljiva)
B_E_ord <- B_E[ordE, ordE]
B_U_ord <- B_U[ordU, ordU]
round(B_E_ord, 3)
## P6 P3 P5 P4 P1 P2
## P6 0.950 0.708 0.188 0.175 0.367 0.606
## P3 0.667 0.513 0.146 0.125 0.042 0.064
## P5 0.088 0.167 0.608 0.023 0.039 0.089
## P4 0.088 0.102 0.031 0.800 0.023 0.027
## P1 0.098 0.029 0.029 0.009 0.048 0.052
## P2 0.194 0.048 0.059 0.029 0.072 0.322
round(B_U_ord, 3)
## P3 P5 P2 P6 P4 P1 P8 P7
## P3 0.800 0.000 0.002 0.000 0.000 0.006 0.000 1.00
## P5 0.000 1.000 0.009 0.000 0.000 0.005 0.286 0.00
## P2 0.003 0.012 0.011 0.043 0.011 0.011 0.004 0.05
## P6 0.000 0.000 0.043 1.000 0.010 0.000 0.000 0.00
## P4 0.000 0.000 0.011 0.010 0.683 0.004 0.000 0.00
## P1 0.006 0.005 0.008 0.000 0.004 0.964 0.000 0.03
## P8 0.000 0.143 0.003 0.000 0.000 0.000 1.000 0.00
## P7 0.267 0.000 0.033 0.000 0.000 0.030 0.000 NA
Pozicije s većim “netflow” su više razine (više šalju prema drugim pozicijama nego što primaju).
Hijerarhijski uzorak znači: veće gustoće se “slijevaju” prema jednoj strani matrice (gore/desno ili dolje/lijevo).
# Ako već imate I_E i I_U (image matrix), samo ih permutirajte:
I_E_ord <- I_E[ordE, ordE]
I_U_ord <- I_U[ordU, ordU]
I_E_ord
## P6 P3 P5 P4 P1 P2
## P6 1 1 0 0 1 1
## P3 1 1 0 0 0 0
## P5 0 0 1 0 0 0
## P4 0 0 0 1 0 0
## P1 0 0 0 0 0 0
## P2 0 0 0 0 0 1
Pozicijska perspektiva hijerarhije kroz blokmodel dodatno pokazuje da se “vertikalnost” ne pojavljuje primarno kao jednostavna gornjetrokutasta matrica gustoća. Nakon poretka pozicija po net-flowu, matrica gustoća i matrica slike pokazuju koncentraciju veza u nekoliko blokova, ali ne i čistu trokutastu strukturu; indeks “trokutastosti” (\(≈0.5\)) sugerira određenu asimetriju prema jednoj strani, no ne dovoljno jasnu da bi se makro-arhitektura svela na uredan hijerarhijski slojeviti poredak pozicija. Drugim riječima, Enronova “hijerarhija” u ovim rezultatima izgleda više kao mješavina gustih unutarnjih blokova i selektivnih među-blok veza, nego kao uredan lanac razina.
I_U_ord
## P3 P5 P2 P6 P4 P1 P8 P7
## P3 1 0 0 0 0 0 0 1
## P5 0 1 0 0 0 0 1 0
## P2 0 0 0 0 0 0 0 0
## P6 0 0 0 1 0 0 0 0
## P4 0 0 0 0 1 0 0 0
## P1 0 0 0 0 0 1 0 0
## P8 0 1 0 0 0 0 1 0
## P7 1 0 0 0 0 0 0 NA
UNIPU-ova matrica slike ne pokazuje obrazac koji bi se mogao čitati kao “hijerarhijski” u smislu trokutaste koncentracije veza iznad ili ispod dijagonale. Dominantan signal je blok-dijagonalnost: većina pozicija ima “1” prvenstveno na vlastitoj dijagonali (P5, P6, P4, P1, P8), što znači da su intra-pozicijske veze dovoljno guste da prijeđu prag binarizacije, dok su veze između različitih pozicija uglavnom ispod praga (0).
Nekoliko izuzetaka ukazuje na selektivne međupozicijske relacije, ali one su simetrično raspoređene i ne tvore stabilan lanac razina. Konkretno, vidljiv je uzajaman odnos P3↔︎P7 (P3→P7 i P7→P3) te odnos P5↔︎P8 (P5→P8 i P8→P5). To je konzistentno s ranijim nalazima o visokoj recipročnosti i općenito sugerira da se pozicije u UNIPU mreži ponašaju kao relativno zatvoreni “funkcionalni blokovi” s nekoliko mostova između odabranih parova blokova, a ne kao strogo poredane razine hijerarhije.
Redak/stupac P2 je posvuda 0, što znači da ta pozicija u matrici slike ne ulazi u “gust” odnos ni s kim pri zadanom pragu (uključujući i samu sebe). To se tipično događa kada je pozicija brojna ali rijetka, ili kada su veze te pozicije disperzirane po više blokova, pa nijedan blok ne prijeđe prag gustoće u binarizaciji. NA u (P7,P7) znači da za taj blok gustoća nije definirana ili je neprocjenjiva (najčešće zbog vrlo male pozicije ili nula mogućih veza nakon odabira/čišćenja), pa taj element ne treba interpretirati sadržajno.
Dakle, UNIPU ovdje izgleda primarno modularno (blokovi + nekoliko selektivnih među-blok veza), a ne hijerarhijski.
Ako želimo jednu brojku (jednostavan indeks “trokutastosti”):
M <- I_E_ord
diag(M) <- NA
up <- sum(M[upper.tri(M)] == 1, na.rm = TRUE)
lo <- sum(M[lower.tri(M)] == 1, na.rm = TRUE)
(up - lo) / (up + lo)
## [1] 0.5
M <- I_U_ord
diag(M) <- NA
up <- sum(M[upper.tri(M)] == 1, na.rm = TRUE)
lo <- sum(M[lower.tri(M)] == 1, na.rm = TRUE)
(up - lo) / (up + lo)
## [1] 0
Zaključno, analiza hijerarhije pretvara apstraktne pojmove poput “utjecaja” u mjerljive varijable koje pomažu predvidjeti stabilnost i učinkovitost ljudskih i tehničkih sustava.
Nakon što smo definirali pozicije i makro-arhitekturu mreže, sljedeći korak jest razumjeti dinamičku funkciju aktera unutar i između tih pozicija. Uloga nije isto što i centralnost niti isto što i pripadnost poziciji. Centralnost mjeri relativnu važnost, pozicija označava strukturnu klasu, dok uloga opisuje način na koji akter posreduje, kontrolira ili usmjerava tokove između drugih aktera ili grupa.
Jedan od najutjecajnijih formalnih pristupa analizi uloga jest model brokerskih pozicija Goulda i Fernandeza (1989). Ovaj pristup promatra usmjerene trijade u kojima jedan akter posreduje između dva druga aktera koji nisu izravno povezani. Ključna je ideja da značenje posredovanja ovisi o grupnoj pripadnosti sudionika.
Pretpostavimo trijadu oblika:
\[ i \rightarrow b \rightarrow j, \]
gdje je \(b\) broker koji posreduje između aktera \(i\) (izvor) i \(j\) (odredište). Uloga brokera određuje se prema tome pripada li isti ili različitim grupama u odnosu na \(i\) i \(j\).
Brokerage označava situaciju u kojoj akter zauzima strukturnu rupu između dviju skupina (Burt, 1992). Takva pozicija omogućuje kontrolu nad protokom informacija, resursa ili utjecaja. No, Gould i Fernandez pokazuju da nisu sve brokerske pozicije jednake: njihovo značenje ovisi o granicama grupa.
Formalno, neka je \(g(x)\) oznaka grupe kojoj akter \(x\) pripada. Tada se uloge razlikuju prema relaciji između \(g(i)\), \(g(b)\) i \(g(j)\).
Koordinator posreduje između dva aktera iste grupe kojoj i sam pripada:
\[ g(i) = g(b) = g(j). \]
On djeluje unutar vlastite zajednice i olakšava koordinaciju među njezinim članovima. Funkcionalno, koordinator doprinosi unutarnjoj koheziji i smanjuje transakcijske troškove komunikacije.
U organizacijskom kontekstu to može biti voditelj tima koji povezuje članove istog odjela. Iako takav akter ne mora biti globalno najcentralniji, njegova je uloga presudna za stabilnost unutar bloka.
Gatekeeper kontrolira ulaz informacija iz druge grupe u vlastitu grupu:
\[ g(i) \neq g(b) = g(j). \]
On prima informacije iz vanjskog sustava i odlučuje koje će se informacije prenijeti unutar njegove grupe. Time gatekeeper ima normativnu i selektivnu moć. Njegova pozicija omogućuje filtriranje, reinterpretaciju ili blokiranje tokova.
U institucionalnim sustavima to može biti urednik, regulator ili menadžer koji odobrava prijedloge izvana. U mrežama znanja, gatekeeper odlučuje koji će vanjski impulsi postati dio lokalne prakse.
Suprotno gatekeeperu, representative prenosi informacije iz vlastite grupe prema van:
\[ g(i) = g(b) \neq g(j). \]
On je glas svoje zajednice prema drugima. Iako strukturalno sličan gatekeeperu, njegova funkcija je eksterna: omogućuje projekciju interesa ili resursa prema drugim blokovima.
Liaison povezuje dvije različite grupe, a da sam ne pripada nijednoj od njih:
\[ g(i) \neq g(b) \neq g(j), \quad g(i) \neq g(j). \]
Ovdje broker djeluje kao neutralna treća strana. On nema interni identitet u odnosu na grupe koje povezuje, što mu može dati veću fleksibilnost, ali i manju legitimnost.
U praksi to može biti konzultant, medijator ili vanjski stručnjak koji povezuje inače nepovezane sustave.
Važno je naglasiti da su brokerske uloge relacijske, a ne inherentne osobine aktera. Isti akter može biti koordinator u jednoj situaciji, a liaison u drugoj, ovisno o strukturi mreže i definiciji grupa.
Broj brokerskih epizoda može se formalno kvantificirati brojanjem svih trijada u kojima akter zauzima određeni položaj. Time se dobiva distribucija uloga unutar mreže.
Brokerage je povezan s konceptom moći jer kontrola nad informacijskim tokovima stvara asimetriju. Akter koji zatvara strukturnu rupu može ostvariti prednost kroz:
Međutim, brokerske pozicije mogu biti i ranjive: ako se uspostavi izravna veza između dviju grupa, uloga brokera nestaje. Stoga je brokerska moć često dinamična i ovisna o stabilnosti strukturnih granica.
U kontekstu blokmodeliranja, uloge se mogu interpretirati kao funkcionalne veze između pozicija. Dok blokmodel definira tko pripada kojoj klasi, analiza uloga pokazuje kako klase međusobno komuniciraju.
Time se zatvara krug:
Uloge stoga predstavljaju operativnu dimenziju pozicijske analize: one pokazuju kako se struktura prevodi u proces, odnosno kako raspored veza generira tokove moći, informacija i utjecaja u mreži.
U prethodnoj lekciji analizirali smo HITS algoritam kao metodu rangiranja čvorova u usmjerenim mrežama. Ovdje ga reinterpretiramo u kontekstu uloga i hijerarhije. HITS (Hyperlink-Induced Topic Search) ne mjeri samo “važnost”, nego diferencira dvije komplementarne strukturne funkcije: autoritet i hub. Time uvodi asimetričnu diferencijaciju uloga koja je posebno relevantna u hijerarhijskim i proto-hijerarhijskim sustavima.
Temeljna ideja HITS algoritma jest kružna definicija:
HITS formalizira razliku između:
U društvenim mrežama znanja, autoritet može biti stručnjak ili institucija koja generira referentne informacije, dok hub može biti akter koji povezuje druge s tim izvorima (npr. urednik, kurator, mentor). U citatnim mrežama autoritet je rad koji se često citira od strane važnih radova, dok je hub rad koji citira ključnu literaturu.
Time HITS diferencira dvije vrste moći:
U kontekstu hijerarhije, HITS omogućuje identifikaciju vertikalne diferencijacije uloga. U idealnoj hijerarhijskoj strukturi (npr. outtree), vršni čvor često ima visoku autoritetnu vrijednost ako prima mnoge veze, dok srednje razine mogu imati visoke hub vrijednosti jer distribuiraju naredbe ili informacije prema nižim razinama.
Ako mreža pokazuje snažnu asimetriju (malo reciprociteta i malo ciklusa), autoriteti i hubovi prirodno se diferenciraju. U mrežama s visokom reciprocitetnošću ili gustoćom, ta se diferencijacija smanjuje jer čvorovi istodobno primaju i šalju velik broj veza.
Stoga HITS može poslužiti kao indikator stupnja funkcionalne diferencijacije unutar usmjerenog sustava.
Iz pozicijske perspektive, autoriteti i hubovi često se koncentriraju u određenim blokovima. Primjerice:
Ako je mreža podijeljena na pozicije \(P_1, \dots, P_K\), moguće je analizirati distribuciju prosječnih autoritetnih i hub vrijednosti po pozicijama:
\[ \bar{a}*k = \frac{1}{|P_k|} \sum*{i \in P_k} a_i, \]
\[ \bar{h}*k = \frac{1}{|P_k|} \sum*{i \in P_k} h_i. \]
Ovdje \(\bar{a}_k\) i \(\bar{h}_k\) predstavljaju prosječnu autoritetnu i hub vrijednost pozicije \(k\), a \(|P_k|\) broj aktera u toj poziciji. Time HITS postaje alat za interpretaciju uloga na razini blokova, a ne samo pojedinaca.
Mreže s izraženim hubovima pokazuju specifičnu topološku dinamiku:
U scale-free mrežama hubovi su posebno kritični za održavanje globalne dostižnosti. Time HITS povezuje koncept uloga s konceptom robusnosti sustava.
HITS je osjetljiv na strukturu podgrafa i može reflektirati lokalne gustoće ili zajednice. Njegove vrijednosti nisu apsolutne mjere važnosti, nego relativne unutar danog konteksta mreže. Stoga interpretacija autoriteta i hubova uvijek mora uzeti u obzir granice mreže i prirodu relacije.
U okviru ove lekcije, HITS predstavlja most između centralnosti i uloga. Dok su klasične mjere centralnosti često simetrične, HITS formalizira asimetričnu diferencijaciju funkcija unutar usmjerenih mreža. Autoritet i hub nisu samo numeričke vrijednosti, nego pozicijske uloge koje proizlaze iz hijerarhijske i funkcionalne organizacije sustava.
library(intergraph)
library(network)
library(sna)
gE <- g_giant
# Identifikator čvorova (ENRON često ima V(g)$Name; igraph standardno koristi V(g)$name)
# U analizi ćemo držati "id" koji je ljudski čitljiv, ali NE mijenjamo nužno V(g)$name.
idE <- if ("Name" %in% vertex_attr_names(gE)) V(gE)$Name else V(gE)$name
# Odabir grupiranja za brokerage
# Najjednostavnije: koristiti pozicije koje smo već dobili (posE).
# Ako ih još nismo upisali u graf:
if (!"pos" %in% vertex_attr_names(gE)) {
# pretpostavka: posE postoji kao vektor duljine vcount(gE)
V(gE)$pos <- posE
}
groupsE <- as.character(V(gE)$pos) # grupe moraju biti vektor po čvorovima
table(groupsE)
## groupsE
## 1 2 3 4 5 6
## 86 35 24 16 16 5
# Gould–Fernandez brokerage (trijade i->b->j, ovisno o grupama)
# Pretvorba igraph -> network (iz intergraph)
edE <- igraph::as_data_frame(gE, what = "edges") %>%
transmute(from = as.character(from),
to = as.character(to))
netE <- network::as.network(edE, directed = TRUE)
# brokerage() računa broj pojavljivanja aktera kao:
# coordinator, gatekeeper, representative, liaison, (i često "itinerant"/"consultant" ovisno o implementaciji)
# Tipično se koristi directed=TRUE ako je mreža usmjerena.
brE <- sna::brokerage(netE, cl = groupsE)
# brE je objekt s tablicama po ulogama (ovisno o verziji sna).
str(brE)
## List of 14
## $ raw.nli: num [1:182, 1:6] 1 38 9 1 36 0 11 17 4 28 ...
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : chr [1:182] "1" "2" "3" "4" ...
## .. ..$ : chr [1:6] "w_I" "w_O" "b_IO" "b_OI" ...
## $ exp.nli: num [1:182, 1:6] 54.16 54.16 54.16 54.16 8.51 ...
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : chr [1:182] "1" "2" "3" "4" ...
## .. ..$ : chr [1:6] "w_I" "w_O" "b_IO" "b_OI" ...
## $ sd.nli : num [1:182, 1:6] 27.06 27.06 27.06 27.06 7.04 ...
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : chr [1:182] "1" "2" "3" "4" ...
## .. ..$ : chr [1:6] "w_I" "w_O" "b_IO" "b_OI" ...
## $ z.nli : num [1:182, 1:6] -1.965 -0.597 -1.669 -1.965 3.905 ...
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : chr [1:182] "1" "2" "3" "4" ...
## .. ..$ : chr [1:6] "w_I" "w_O" "b_IO" "b_OI" ...
## $ raw.gli: Named num [1:6] 3368 7604 7593 7403 17588 ...
## ..- attr(*, "names")= chr [1:6] "w_I" "w_O" "b_IO" "b_OI" ...
## $ exp.gli: Named num [1:6] 5100 7944 7944 7944 16052 ...
## ..- attr(*, "names")= chr [1:6] "w_I" "w_O" "b_IO" "b_OI" ...
## $ sd.gli : Named num [1:6] 338 357 340 340 674 ...
## ..- attr(*, "names")= chr [1:6] "w_I" "w_O" "b_IO" "b_OI" ...
## $ z.gli : Named num [1:6] -5.121 -0.952 -1.031 -1.59 2.279 ...
## ..- attr(*, "names")= chr [1:6] "w_I" "w_O" "b_IO" "b_OI" ...
## $ exp.grp: num [1:6, 1:6] 54.16 8.51 3.84 1.59 1.59 ...
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : chr [1:6] "1" "2" "3" "4" ...
## .. ..$ : chr [1:6] "w_I" "w_O" "b_IO" "b_OI" ...
## $ sd.grp : num [1:6, 1:6] 27.06 7.04 4 2.17 2.17 ...
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : chr [1:6] "1" "2" "3" "4" ...
## .. ..$ : chr [1:6] "w_I" "w_O" "b_IO" "b_OI" ...
## $ cl : chr [1:182] "1" "1" "1" "1" ...
## $ clid : chr [1:6] "1" "2" "3" "4" ...
## $ n : num [1:6] 86 35 24 16 16 5
## $ N : num 182
## - attr(*, "class")= chr "brokerage"
# node-level brokerage counts (po akteru)
brE_df <- data.frame(
id = idE,
group = groupsE,
brE$raw.nli,
row.names = NULL,
check.names = FALSE
)
head(brE_df)
## id group w_I w_O b_IO b_OI b_O t
## 1 Albert Meyers 1 1 1 3 2 1 8
## 2 Thomas Martin 1 38 2 23 35 20 118
## 3 Andrea Ring 1 9 12 9 21 11 62
## 4 Andrew Lewis 1 1 1 1 3 1 7
## 5 Andy Zipper 2 36 64 101 117 235 553
## 6 Jeffrey Shankman 3 0 15 5 9 27 56
brE_df <- dplyr::rename(
brE_df,
coordinator = w_I,
gatekeeper = w_O,
representative= b_IO,
liaison = b_OI
)
# top 10 koordinatora / gatekeepera / predstavnika / liaison
brE_df %>% dplyr::arrange(dplyr::desc(coordinator)) %>% head(10)
## id group coordinator gatekeeper representative liaison b_O t
## 1 NA 1 411 312 752 712 920 3107
## 2 John Forney 1 358 90 369 448 359 1624
## 3 NA 2 253 1026 950 771 1798 4798
## 4 Kate Symes 1 178 70 212 253 200 913
## 5 NA 1 147 57 135 209 131 679
## 6 Joe Quenet 1 125 77 184 248 266 900
## 7 NA 1 122 59 230 153 218 782
## 8 Andrew Lewis 1 90 15 71 144 70 390
## 9 Mike Carson 1 71 6 61 37 26 201
## 10 Don Baughman 1 68 13 46 49 10 186
brE_df %>% dplyr::arrange(dplyr::desc(gatekeeper)) %>% head(10)
## id group coordinator gatekeeper representative liaison b_O t
## 1 NA 2 253 1026 950 771 1798 4798
## 2 Rod Hayslett 4 0 491 31 30 856 1408
## 3 Rick Buy 3 40 424 232 260 923 1879
## 4 NA 1 411 312 752 712 920 3107
## 5 Stacy Dickson 5 4 250 98 57 543 952
## 6 Jason Wolfe 2 21 191 108 92 226 638
## 7 David Delainey 3 8 178 130 48 384 748
## 8 Barry Tycholiz 2 11 167 76 75 384 713
## 9 Gerald Nemec 5 1 163 49 21 304 538
## 10 NA 3 10 162 76 86 312 646
brE_df %>% dplyr::arrange(dplyr::desc(representative)) %>% head(10)
## id group coordinator gatekeeper representative liaison b_O t
## 1 NA 2 253 1026 950 771 1798 4798
## 2 NA 1 411 312 752 712 920 3107
## 3 John Forney 1 358 90 369 448 359 1624
## 4 Randall Gay 2 51 153 304 115 473 1096
## 5 Rick Buy 3 40 424 232 260 923 1879
## 6 NA 1 122 59 230 153 218 782
## 7 Kate Symes 1 178 70 212 253 200 913
## 8 Joe Quenet 1 125 77 184 248 266 900
## 9 NA 1 147 57 135 209 131 679
## 10 David Delainey 3 8 178 130 48 384 748
brE_df %>% dplyr::arrange(dplyr::desc(liaison)) %>% head(10)
## id group coordinator gatekeeper representative liaison b_O t
## 1 NA 2 253 1026 950 771 1798 4798
## 2 NA 1 411 312 752 712 920 3107
## 3 John Forney 1 358 90 369 448 359 1624
## 4 Rick Buy 3 40 424 232 260 923 1879
## 5 Kate Symes 1 178 70 212 253 200 913
## 6 Joe Quenet 1 125 77 184 248 266 900
## 7 NA 1 147 57 135 209 131 679
## 8 NA 1 122 59 230 153 218 782
## 9 Andrew Lewis 1 90 15 71 144 70 390
## 10 Andy Zipper 2 36 64 101 117 235 553
Iščitavanje:
(g(i)=g(b)=g(j)) -> “unutarnja koordinacija”(g(i)≠g(b)=g(j))
-> “kontrola ulaza”(g(i)=g(b)≠g(j)) -> “glas prema van”(g(i)≠g(b)≠g(j), g(i)≠g(j)) -> “posredovanje među
blokovima”Tumačenje:
Ulogama posredovanja (Gould–Fernandez brokerage) dobiva se druga dimenzija strukture: akteri se pojavljuju kao brokeri u trijadama i→b→j ovisno o tome pripadaju li isti ili različitim pozicijama. Visoke vrijednosti koordinatora impliciraju snažno posredovanje unutar iste pozicije (intra-blok koordinacija), dok visoki gatekeeper i representative ukazuju na kontrolu “granica” pozicije, tj. na čvorove kroz koje ulaze ili izlaze relacije između pozicija. U Enronu su u vrhovima lista prisutni i nedostajući identifikatori (“NA”), što je analitički važan signal o kvaliteti metapodataka: ti čvorovi mogu biti tehničke adrese, liste, ili zapisi bez imena, pa njihovu interpretaciju kao “osoba” treba ograničiti. Unatoč tome, raspodjela brokerstva po pozicijama je informativna jer pokazuje da određene pozicije nose nesrazmjeran dio međublok posredovanja, što je tipično za komunikacijske mreže gdje mali broj aktera ili adresa stabilno kanalizira razmjenu među segmentima organizacije.
# HITS: authority i hub (igraph)
hitsE_auth <- igraph::authority_score(gE)$vector
hitsE_hub <- igraph::hub_score(gE)$vector
V(gE)$hits_auth <- hitsE_auth
V(gE)$hits_hub <- hitsE_hub
# Top autoriteti i top hubovi
E_auth_top <- head(order(hitsE_auth, decreasing = TRUE), 10)
E_hub_top <- head(order(hitsE_hub, decreasing = TRUE), 10)
data.frame(id = idE[E_auth_top], group = groupsE[E_auth_top], auth = hitsE_auth[E_auth_top])
## id group auth
## 1 John Lavorato 6 1.0000000
## 2 Louise Kitchen 6 0.9420741
## 3 Greg Whalley 3 0.7995592
## 4 Scott Neal 2 0.7584562
## 5 Michael Grigsby 2 0.7202247
## 6 David Delainey 3 0.7065892
## 7 John Arnold 2 0.6631496
## 8 Andy Zipper 2 0.6156507
## 9 Steven Kean 3 0.6130554
## 10 Rick Buy 3 0.6098001
data.frame(id = idE[E_hub_top], group = groupsE[E_hub_top], hub = hitsE_hub[E_hub_top])
## id group hub
## 1 John Lavorato 6 1.0000000
## 2 NA 6 0.9155274
## 3 Louise Kitchen 6 0.8655132
## 4 Sally Beck 6 0.8401255
## 5 Kenneth Lay 6 0.6278173
## 6 Jeff Dasovich 3 0.5177157
## 7 David Delainey 3 0.4957444
## 8 Michael Grigsby 2 0.4808434
## 9 John Arnold 2 0.4721971
## 10 Scott Neal 2 0.4402708
# HITS po grupama (pozicijska interpretacija)
# Prosječni autoritet/hub unutar pozicija
aggregate(hitsE_auth, by = list(group = groupsE), FUN = mean)
## group x
## 1 1 0.1760920
## 2 2 0.3645630
## 3 3 0.4857725
## 4 4 0.2011937
## 5 5 0.2563166
## 6 6 0.6842747
aggregate(hitsE_hub, by = list(group = groupsE), FUN = mean)
## group x
## 1 1 0.06975407
## 2 2 0.21439695
## 3 3 0.33912847
## 4 4 0.12893044
## 5 5 0.19797428
## 6 6 0.84979670
HITS (authority i hub) u Enronu jasno diferencira pozicije: prosječni authority i hub najviši su u jednoj poziciji (grupa 6), što je kompatibilno s interpretacijom te pozicije kao strukturne “jezgre” informacijske validacije i distribucije. Autoriteti su čvorovi na koje “pokazuju” hubovi, pa visoke authority vrijednosti sugeriraju da su ti akteri česte mete komunikacije onih koji puno šalju; visoki hubovi, obratno, sugeriraju čvorove koji intenzivno upućuju prema autoritetima. U komunikacijskom kontekstu to se može čitati kao kombinacija prestiža (primanje) i kapaciteta za usmjeravanje ili koordinaciju (slanje prema relevantnima), pri čemu se sadržaj komunikacije ne pretpostavlja, nego se zaključuje isključivo iz topologije usmjerenih veza.
gU <- g_weak_giant
idU <- V(gU)$name # UNIPU ima anonimizirani ID u "name"
# Grupa A: "formalno" (sastavnica nastavnika)
# (Napomena: kod vas sastavnica nastavnika je atribut čvora: V(gU)$sast_nast)
groupsU_formal <- as.character(V(gU)$sast_nast)
groupsU_formal[is.na(groupsU_formal) | groupsU_formal == ""] <- "vanjski"
table(groupsU_formal)
## groupsU_formal
## fet ffpu fipu fipu | tfpu fooz fpz
## 37 48 15 1 23 7
## mapu mfpu tfpu vanjski
## 8 10 10 185
# Grupa B: "po kompetenciji/stručnosti" (pozicije iz strukturne sličnosti)
# Ako posU već postoji:
if (!"pos" %in% vertex_attr_names(gU)) {
# pretpostavka: posU postoji kao vektor duljine vcount(gU)
V(gU)$pos <- posU
}
groupsU_pos <- as.character(V(gU)$pos)
table(groupsU_pos)
## groupsU_pos
## 1 2 3 4 5 6 7 8
## 33 242 15 24 18 4 1 7
# Gould–Fernandez brokerage (trijade i->b->j, ovisno o grupama)
# Pretvorba igraph -> network (iz intergraph)
edU <- igraph::as_data_frame(gU, what = "edges") %>%
transmute(from = as.character(from),
to = as.character(to))
netU <- network::as.network(edU, directed = TRUE)
# 3a) brokerage prema sastavnicama (formalna podjela)
brU_formal <- sna::brokerage(netU, cl = groupsU_formal)
brU_df <- data.frame(
id = idU,
group = groupsU_formal,
brU_formal$raw.nli,
row.names = NULL,
check.names = FALSE
)
brU_df <- dplyr::rename(
brU_df,
coordinator = w_I,
gatekeeper = w_O,
representative= b_IO,
liaison = b_OI
)
# top 10 koordinatora / gatekeepera / predstavnika / liaison
brU_df %>% dplyr::arrange(dplyr::desc(coordinator)) %>% head(10)
## id group coordinator gatekeeper representative liaison b_O t
## 1 110 ffpu 166 38 95 121 14 434
## 2 223 ffpu 123 36 75 84 0 318
## 3 075 fet 111 38 69 91 8 317
## 4 052 fet 111 47 69 114 10 351
## 5 328 fet 106 31 51 85 8 281
## 6 142 fet 82 54 202 177 120 635
## 7 353 ffpu 82 14 42 33 0 171
## 8 273 ffpu 81 0 12 12 0 105
## 9 026 ffpu 81 4 16 24 0 125
## 10 127 fet 73 16 50 29 4 172
brU_df %>% dplyr::arrange(dplyr::desc(gatekeeper)) %>% head(10)
## id group coordinator gatekeeper representative liaison b_O t
## 1 210 tfpu 7 163 47 42 25 284
## 2 247 fet 0 156 142 160 30 488
## 3 045 tfpu 3 139 35 32 24 233
## 4 225 fet 28 116 243 199 148 734
## 5 069 mfpu 19 94 70 70 62 315
## 6 181 vanjski 0 71 13 13 8 105
## 7 346 vanjski 33 68 65 58 0 224
## 8 029 mfpu 13 67 42 33 25 180
## 9 185 tfpu 3 67 15 11 0 96
## 10 204 fooz 34 65 63 120 105 387
brU_df %>% dplyr::arrange(dplyr::desc(representative)) %>% head(10)
## id group coordinator gatekeeper representative liaison b_O t
## 1 225 fet 28 116 243 199 148 734
## 2 142 fet 82 54 202 177 120 635
## 3 058 fipu 5 34 154 3 88 284
## 4 247 fet 0 156 142 160 30 488
## 5 110 ffpu 166 38 95 121 14 434
## 6 223 ffpu 123 36 75 84 0 318
## 7 069 mfpu 19 94 70 70 62 315
## 8 075 fet 111 38 69 91 8 317
## 9 052 fet 111 47 69 114 10 351
## 10 214 vanjski 38 0 66 44 0 148
brU_df %>% dplyr::arrange(dplyr::desc(liaison)) %>% head(10)
## id group coordinator gatekeeper representative liaison b_O t
## 1 225 fet 28 116 243 199 148 734
## 2 142 fet 82 54 202 177 120 635
## 3 247 fet 0 156 142 160 30 488
## 4 110 ffpu 166 38 95 121 14 434
## 5 204 fooz 34 65 63 120 105 387
## 6 052 fet 111 47 69 114 10 351
## 7 208 fet 28 48 66 105 34 281
## 8 075 fet 111 38 69 91 8 317
## 9 328 fet 106 31 51 85 8 281
## 10 223 ffpu 123 36 75 84 0 318
# 3b) brokerage prema pozicijama (kompetencijsko/stručno grupiranje)
brU_pos <- sna::brokerage(netU, cl = groupsU_pos)
brU_df <- data.frame(
id = idU,
group = groupsU_formal,
brU_pos$raw.nli,
row.names = NULL,
check.names = FALSE
)
brU_df <- dplyr::rename(
brU_df,
coordinator = w_I,
gatekeeper = w_O,
representative= b_IO,
liaison = b_OI
)
# top 10 koordinatora / gatekeepera / predstavnika / liaison
brU_df %>% dplyr::arrange(dplyr::desc(coordinator)) %>% head(10)
## id group coordinator gatekeeper representative liaison b_O t
## 1 119 ffpu 154 1 11 55 0 221
## 2 309 ffpu 153 0 0 0 0 153
## 3 224 ffpu 153 0 0 23 0 176
## 4 109 ffpu 120 2 41 42 0 205
## 5 307 fipu 114 0 27 16 2 159
## 6 179 fooz 70 0 11 5 0 86
## 7 089 fpz 65 0 5 7 0 77
## 8 314 ffpu 62 0 6 0 0 68
## 9 269 ffpu 45 6 37 28 8 124
## 10 087 ffpu 44 0 0 0 0 44
brU_df %>% dplyr::arrange(dplyr::desc(gatekeeper)) %>% head(10)
## id group coordinator gatekeeper representative liaison b_O t
## 1 069 mfpu 0 196 44 43 32 315
## 2 029 mfpu 0 132 23 25 0 180
## 3 210 tfpu 0 88 0 0 196 284
## 4 225 fet 38 59 344 280 13 734
## 5 223 ffpu 0 54 134 130 0 318
## 6 110 ffpu 27 49 131 214 13 434
## 7 204 fooz 0 47 99 195 46 387
## 8 348 vanjski 0 35 9 8 0 52
## 9 154 vanjski 0 30 9 7 0 46
## 10 247 fet 0 28 210 237 13 488
brU_df %>% dplyr::arrange(dplyr::desc(representative)) %>% head(10)
## id group coordinator gatekeeper representative liaison b_O t
## 1 225 fet 38 59 344 280 13 734
## 2 142 fet 0 26 310 275 24 635
## 3 247 fet 0 28 210 237 13 488
## 4 223 ffpu 0 54 134 130 0 318
## 5 110 ffpu 27 49 131 214 13 434
## 6 075 fet 38 12 118 149 0 317
## 7 052 fet 38 18 118 177 0 351
## 8 068 vanjski 0 0 108 90 0 198
## 9 346 vanjski 0 25 103 96 0 224
## 10 026 ffpu 0 22 100 3 0 125
brU_df %>% dplyr::arrange(dplyr::desc(liaison)) %>% head(10)
## id group coordinator gatekeeper representative liaison b_O t
## 1 225 fet 38 59 344 280 13 734
## 2 142 fet 0 26 310 275 24 635
## 3 058 fipu 0 6 31 247 0 284
## 4 247 fet 0 28 210 237 13 488
## 5 110 ffpu 27 49 131 214 13 434
## 6 204 fooz 0 47 99 195 46 387
## 7 328 fet 0 11 90 180 0 281
## 8 052 fet 38 18 118 177 0 351
## 9 075 fet 38 12 118 149 0 317
## 10 208 fet 38 6 87 146 4 281
Brokerage analiza na UNIPU-u je posebno osjetljiva na odabir
grupiranja, pa je smisleno računati ju i prema formalnim sastavnicama i
prema pozicijama. Kada se grupe definiraju formalno (sastavnice), visoke
vrijednosti gatekeepera i representative tipično identificiraju aktere
koji strukturirano povezuju svoju sastavnicu s ostatkom sustava; u ovom
kontekstu to su potencijalni koordinatori suradnje “preko granica”
organizacijskih jedinica. Kada se grupe definiraju pozicijski
(posU), brokerstvo hvata posredovanje između
kompetencijskih/strukturnih blokova, pa se u vrhovima pojavljuju akteri
koji nisu nužno formalno na “granici” sastavnice, nego su strukturno u
ulozi spajanja tipova angažmana. U oba slučaja, činjenica da se vrhovi
liaison uloga poklapaju s visokim representative vrijednostima sugerira
da se ključni posrednici pojavljuju i kao “glas” vlastite skupine prema
van i kao međublok posrednici u širem smislu.
# HITS na UNIPU (authority/hub)
hitsU_auth <- igraph::authority_score(gU)$vector
hitsU_hub <- igraph::hub_score(gU)$vector
V(gU)$hits_auth <- hitsU_auth
V(gU)$hits_hub <- hitsU_hub
# Top autoriteti / hubovi
U_auth_top <- head(order(hitsU_auth, decreasing = TRUE), 10)
U_hub_top <- head(order(hitsU_hub, decreasing = TRUE), 10)
data.frame(id = idU[U_auth_top], sastavnica = groupsU_formal[U_auth_top], auth = hitsU_auth[U_auth_top])
## id sastavnica auth
## 274 274 fet 1.0000000
## 181 181 vanjski 0.8969055
## 165 165 fet 0.8793509
## 208 208 fet 0.7662269
## 011 011 fet 0.7346762
## 114 114 vanjski 0.7278885
## 170 170 fet 0.6717632
## 159 159 vanjski 0.6500824
## 334 334 fet 0.6256583
## 293 293 vanjski 0.6228406
data.frame(id = idU[U_hub_top], sastavnica = groupsU_formal[U_hub_top], hub = hitsU_hub[U_hub_top])
## id sastavnica hub
## 274 274 fet 1.0000000
## 165 165 fet 0.9878248
## 181 181 vanjski 0.9275276
## 001 001 fet 0.8232420
## 208 208 fet 0.7949074
## 011 011 fet 0.7685031
## 114 114 vanjski 0.7600358
## 170 170 fet 0.7495497
## 159 159 vanjski 0.7049028
## 064 064 fet 0.6783699
# HITS po pozicijama (kompetencijska/stručna perspektiva)
aggregate(hitsU_auth, by = list(pos = groupsU_pos), FUN = mean)
## pos x
## 1 1 0.5147529558
## 2 2 0.0074871816
## 3 3 0.0059103476
## 4 4 0.0020530311
## 5 5 0.0030780623
## 6 6 0.0003245411
## 7 7 0.0243641543
## 8 8 0.0005416359
aggregate(hitsU_hub, by = list(pos = groupsU_pos), FUN = mean)
## pos x
## 1 1 0.5571718298
## 2 2 0.0101791510
## 3 3 0.0069018515
## 4 4 0.0022732618
## 5 5 0.0032128644
## 6 6 0.0003116144
## 7 7 0.0127719886
## 8 8 0.0003395191
HITS u UNIPU mreži pokazuje izrazitu koncentraciju authority/hub vrijednosti u malom broju čvorova i u jednoj poziciji: prosječne vrijednosti po pozicijama sugeriraju da je jedna pozicija daleko iznad ostalih po oba indeksa. U usmjerenim mrežama HITS često izdvaja “čvorišta” (hubove) koja upućuju prema istaknutim akterima (autoritetima), i “autoritet” čvorove koji primaju veze od relevantnih hubova. U UNIPU kontekstu, to se može interpretirati kao strukturno izdvajanje aktera koji su istovremeno intenzivno uključeni u relacije prema mnogima i centralni kao mete relacija, što je kompatibilno s ulogama visokog koordinacijskog opterećenja ili formalne odgovornosti, ali sama mjera ne razlučuje je li to posljedica formalne hijerarhije ili organizacijskog “hubiranja” suradnje. Prosjeci po sastavnicama dodatno sugeriraju da određene sastavnice nose veći dio te HITS strukture, dok kategorija “vanjski” ima nenultu prosječnu vrijednost, što je indikativno za vanjske aktere koji su uvezani s ključnim internim čvorovima.
# HITS po sastavnicama (formalna perspektiva)
aggregate(hitsU_auth, by = list(sast = groupsU_formal), FUN = mean)
## sast x
## 1 fet 3.490858e-01
## 2 ffpu 2.925704e-03
## 3 fipu 5.462054e-02
## 4 fipu | tfpu 7.691712e-04
## 5 fooz 1.901050e-03
## 6 fpz 4.080607e-04
## 7 mapu 1.219846e-05
## 8 mfpu 1.974417e-04
## 9 tfpu 8.583086e-03
## 10 vanjski 2.708304e-02
aggregate(hitsU_hub, by = list(sast = groupsU_formal), FUN = mean)
## sast x
## 1 fet 3.845853e-01
## 2 ffpu 3.221966e-03
## 3 fipu 6.369243e-02
## 4 fipu | tfpu 9.480458e-04
## 5 fooz 1.919989e-03
## 6 fpz 4.232417e-04
## 7 mapu 2.578121e-05
## 8 mfpu 2.037721e-04
## 9 tfpu 8.384622e-03
## 10 vanjski 3.031610e-02
U oba sustava, visoka recipročnost i dominantne jake komponente znače da “hijerarhija” u strogo DAG smislu nije empirijski adekvatan opis, pa je kondenzacija u DAG najprimjereniji kompromis: hijerarhiju je smislenije promatrati kao odnos među cikličkim blokovima nego među pojedinačnim akterima. Enron pritom pokazuje grublju makro-strukturu (malo SCC blokova), dok UNIPU pokazuje razgranatiju kondenziranu strukturu (više blokova), što je kompatibilno s institucionalno segmentiranijim sustavom. Krackhardtova hijerarhija je niska u oba slučaja, ali je UNIPU ekstremniji primjer zbog gotovo potpune recipročnosti, dok Enron zadržava nešto više asimetrije i time nešto više “vertikalnog signala”, iako još uvijek daleko od idealne hijerarhije.
stats, rlang,
utils) djeluju kao infrastrukturni autoriteti.
U prethodnim lekcijama prijelaz faze (phase transition) analiziran je kao globalni fenomen rasta mreže: u modelima tipa Erdős–Rényi, pri povećanju vjerojatnosti veze \(p\) dolazi do nagle promjene strukture kada se pojavi gigantska komponenta. U ovom poglavlju taj koncept reinterpretiramo iz perspektive pozicija i uloga. Ključno pitanje glasi: kada i kako mreža počinje generirati stabilne strukturne pozicije?
U nasumičnoj mreži s \(n\) čvorova i vjerojatnošću veze \(p\), očekivani broj veza iznosi približno:
\[ m \approx \frac{n(n-1)}{2} p. \]
Ključni parametar je očekivani stupanj:
\[ \langle k \rangle = p(n-1). \]
Kada vrijedi:
\[ \langle k \rangle \approx 1, \]
dolazi do kritične točke u kojoj se mala, fragmentirana komponentna struktura transformira u sustav s jednom dominantnom komponentom. Ovaj nagli kvalitativni skok naziva se prijelaz faze.
Do tog trenutka mreža je skup izoliranih ili slabo povezanih klastera; nakon prijelaza, većina čvorova postaje dio iste relacijske cjeline.
Za pozicijsku analizu ključno je sljedeće: pozicije postaju stabilne tek kada mreža ima dovoljno relacijskog materijala. U izrazito rijetkim mrežama nema dovoljno strukture da bi se razvile diferencirane uloge. Veze su sporadične i ne generiraju sustavne obrasce.
Tek nakon prelaska kritične gustoće pojavljuju se:
Drugim riječima, prijelaz faze ne stvara samo povezanost, nego omogućuje nastanak strukturne nejednakosti.
U modelima preferencijalnog povezivanja (Barabási–Albert), novi čvorovi imaju veću vjerojatnost povezivanja s već povezanima. Time se spontano formiraju hubovi, a mreža poprima skalno-slobodnu distribuciju stupnjeva.
Ovaj proces vodi diferencijaciji pozicija:
Pozicije ovdje nisu unaprijed zadane, nego emergentne — nastaju iz lokalnih pravila rasta.
Regularna ekvivalencija podrazumijeva postojanje stabilnih tipova odnosa (npr. nadređeni–podređeni, proizvođač–potrošač). Takva struktura zahtijeva dovoljnu gustoću i rekurzivnost odnosa.
Ako mreža nije prešla prag povezanosti, rekurzivna definicija regularne ekvivalencije nema empirijsku podlogu, jer nema stabilnih klasa aktera. Nakon prijelaza faze, međutim, formiraju se obrasci koji omogućuju grupiranje u blokove i identifikaciju uloga.
Stoga se može reći:
Phase transition je uvjet mogućnosti pozicijske diferencijacije.
U usmjerenim mrežama povećanje gustoće može dovesti do pojave acikličnih dominacijskih struktura ili, suprotno, do povećanja reciprociteta i cikličnosti. U ranoj fazi mreže teško je govoriti o hijerarhiji jer nema dovoljno relacija da bi se formirala jasna vertikalna struktura.
Tek kada mreža postane dovoljno povezana:
Hijerarhija je stoga sekundarni fenomen — rezultat kumulativnog rasta i reorganizacije mreže nakon kritične točke.
Prijelaz faze naglašava da pozicije nisu statične. Kako mreža raste ili gubi veze:
Pozicijska analiza stoga nije samo statička klasifikacija, nego snimka sustava u određenom trenutku razvoja.
Za ilustraciju, koristimo prikaz kroz rast Erdős–Rényi mreže tako da dodajemo bridove jedan po jedan (prijelaz faze oko \(⟨k⟩≈1\)).Ideja: dok je ⟨k⟩ < 1 mreža je fragmentirana; kako se ⟨k⟩ približava 1, naglo raste najveća komponenta. Nakon toga često raste i “strukturni signal” (npr. modularnost), pa zajednice/pozicije postaju stabilnije.
set.seed(1)
# Parametri
n <- 120 # broj čvorova
m_max <- 400 # koliko bridova ukupno dodajemo
snap_at <- c(seq(1, 9, by = 1), seq(10, 49, by = 10), seq(50, 149, by= 20), seq(150, m_max, by = 50))
# (1) pripremimo sve moguće bridove (neusmjereno, bez self-loop)
all_pairs <- t(combn(seq_len(n), 2))
all_pairs <- all_pairs[sample(nrow(all_pairs)), ] # random redoslijed
# (2) inicijalni graf (bez bridova)
g <- make_empty_graph(n = n, directed = FALSE)
lay <- layout_with_fr(g) # fiksiramo layout radi usporedbe kroz vrijeme
# pomoćne "minimalne" metrike po snimci
get_stats <- function(g) {
# prosječni stupanj
k_avg <- mean(igraph::degree(g))
# udio najveće komponente
comp <- igraph::components(g)
giant_share <- max(comp$csize) / igraph::vcount(g)
# modularnost (na neusmjerenoj mreži)
if (igraph::ecount(g) == 0) {
mod <- NA_real_
n_comm <- NA_integer_
} else {
comm <- igraph::cluster_louvain(g)
mod <- igraph::modularity(comm)
n_comm <- length(igraph::sizes(comm))
}
c(k_avg = k_avg, giant_share = giant_share, modularity = mod, n_comm = n_comm)
}
stats <- data.frame(m = integer(), k_avg = double(), giant_share = double(),
modularity = double(), n_comm = integer())
# za sličan grafički prikaz onome iz animacije ispod
for (m in 1:m_max) {
# dodajemo po 1 brid
e <- all_pairs[m, ]
g <- add_edges(g, c(e[1], e[2]))
# ako je m snimka
if (m %in% snap_at) {
s <- get_stats(g)
stats <- rbind(stats, data.frame(m = m, t(s)))
# obojaj najveću komponentu (vizualni “klik” prijelaza faze)
comp <- igraph::components(g)
giant_id <- which.max(comp$csize)
col_v <- ifelse(comp$membership == giant_id, "black", "grey80")
frame_i <- frame_i + 1
fn <- sprintf("frames_ge/frame_%03d.png", frame_i)
png(fn, width = 1200, height = 800, res = 150)
plot(g,
layout = lay,
vertex.size = 4,
vertex.label = NA,
vertex.color = col_v,
edge.color = "grey70",
main = paste0("m = ", m,
" | <k> = ", round(s["k_avg"], 2),
"\nGiant_comp_udio = ", round(s["giant_share"], 2)))
dev.off()
}
}
par(mfrow = c(1, 2))
plot(stats$m, stats$giant_share, type = "b", pch = 16,
xlab = "broj bridova m", ylab = "udio najveće komponente",
main = "Phase transition: nastanak gigantske komponente")
plot(stats$m, stats$modularity, type = "b", pch = 16,
xlab = "broj bridova m", ylab = "modularnost (Louvain)",
main = "Signal pozicija: modularnost zajednica")
Uvođenje prijelaza faze u analizu pozicija ima važnu teorijsku implikaciju: uloge nisu inherentne osobinama aktera, nego proizlaze iz globalne konfiguracije sustava. Tek kada mreža postigne određeni stupanj kompleksnosti, moguće je govoriti o stabilnim pozicijama poput jezgre, periferije, brokera ili autoriteta.
Time se zatvara konceptualni luk ove teme:
Phase transition tako predstavlja most između dinamike rasta i strukturne organizacije mreže — trenutak u kojem kvantitativna promjena (više veza) prelazi u kvalitativnu promjenu (nastanak društvene strukture).
U prethodnim poglavljima analizirali smo kako se iz relacijskih podataka mogu izdvojiti pozicije, uloge i makro-arhitektura mreže. No, svaki takav rezultat ovisi o načinu na koji je mreža konstruirana. Koncept dualnosti i problem granica mreže (boundary problem) podsjećaju nas da mrežna analiza nije neutralna reprodukcija stvarnosti, nego modeliranje određenog aspekta sustava.
Breiger (1974) pokazuje da društvena struktura ima dvostruku narav: akteri su povezani kroz aktivnosti, a aktivnosti su povezane kroz aktere. To znači da svaka jednopartitna mreža (npr. mreža suradnje među studentima) može biti promatrana kao projekcija dvomodalne mreže (npr. studenti–kolegiji).
Formalno, neka je \(B\) binarna matrica afilijacije dimenzija \(n \times m\), gdje redci predstavljaju aktere, a stupci događaje ili aktivnosti. Element \(b_{ik} = 1\) označava da akter \(i\) sudjeluje u aktivnosti \(k\).
Iz takve matrice moguće je konstruirati:
\[ A = B B^T, \]
\[ C = B^T B. \]
Matrica \(A\) prikazuje koliko akteri dijele zajedničkih aktivnosti, dok \(C\) prikazuje koliko aktivnosti dijele iste sudionike. Ovdje se jasno vidi dualnost: ono što interpretiramo kao “vezu” u jednoj projekciji zapravo je posljedica zajedničkog sudjelovanja u drugoj dimenziji.
Time se postavlja ključno pitanje: jesu li pozicije svojstvo aktera ili aktivnosti? Odgovor ovisi o razini analize.
Modeliranje omogućuje:
Blokmodeliranje, primjerice, komprimira mrežu s \(n\) čvorova na \(K\) pozicija (\(K \ll n\)), čime se omogućuje uvid u makro-arhitekturu sustava. HITS diferencira autoritete i hubove; brokerage otkriva kontrolne točke; hijerarhijske mjere kvantificiraju vertikalnu strukturu.
Bez modeliranja, takve regularnosti bile bi nevidljive u mnoštvu pojedinačnih veza.
Svaka redukcija podrazumijeva gubitak informacije. Kada definiramo pozicije, implicitno pretpostavljamo da su akteri unutar iste pozicije dovoljno slični da se mogu tretirati kao klasa. Time zanemarujemo:
Blokmodeliranje pretvara raznolike interakcije u agregirane gustoće. HITS sažima složene tokove u dvije numeričke vrijednosti. Hijerarhijski modeli pretpostavljaju acikličnost i jasnoću dominacije, iako stvarni sustavi često uključuju reciprocitet i pregovaranje.
Model je, dakle, apstrakcija — a apstrakcija uvijek selektira.
Jedno od najvažnijih metodoloških pitanja jest: tko je uključen u mrežu, a tko nije?
Granice se mogu definirati:
Pogrešno postavljena granica može:
Primjerice, ako iz mreže izostavimo vanjske partnere, interna struktura može izgledati hijerarhijski, iako je u širem kontekstu horizontalno integrirana.
Kod dvomodalnih mreža projekcija na jednopartitnu mrežu može proizvesti lažnu gustoću ili umjetne klastere. Ako više aktera sudjeluje u istoj velikoj aktivnosti, projekcija može stvoriti potpun blok (complete block), iako među njima ne postoji izravna interakcija.
Time dualnost pokazuje da:
Iz perspektive ove lekcije, najvažnija implikacija glasi:
Pozicije i uloge nisu ontološke kategorije, nego relacijski konstrukt definiran unutar određenog modela.
Hijerarhija, jezgra, broker, autoritet — sve su to interpretacije obrasca veza unutar određenih granica i pretpostavki. Promjena razine analize (npr. s aktera na događaje), promjena granice ili promjena praga gustoće može promijeniti i samu klasifikaciju.
Dualnost i granice modeliranja zatvaraju konceptualni luk ove cjeline. U početku smo se pitali zašto centralnost nije dovoljna. Zatim smo definirali ekvivalencije, pozicije, blokove, hijerarhiju i uloge. Sada uviđamo da su sve te kategorije rezultat:
Mrežna analiza omogućuje iznimno precizno matematičko modeliranje društvene strukture, ali njezina snaga proizlazi upravo iz svijesti o vlastitim ograničenjima. Dualnost nas podsjeća da je svaka mreža istodobno i druga mreža — ovisno o tome što tretiramo kao čvor, a što kao vezu.
Time analiza pozicija i uloga postaje ne samo tehnički alat, nego i teorijski okvir za razumijevanje kako se struktura konstruira, interpretira i transformira.
Uvijek je moguće: iz jednopartitne mreže napraviti bipartitnu čvor–brid (node–edge incidence), pa se projekcijom vratiti natrag. Ovo radi i za Enron i za Unipu, bez ikakvih dodatnih podataka.
Breiger dualnost (akter–aktivnost): iz bipartitne mreže (npr.
osobe–kolegiji) napraviti dvije projekcije: mrežu osoba i mrežu
kolegija. To je “prava” dualnost koju opisuje (\(BB^T\)) i (\(B^T
B\)). Ovo je prirodno pokazati na UNIPU, jer imamo
predmet (kolegij) kao atribut.
Osnovna ideja: svaki brid postane jedan “event-čvor”. Povežemo (\(u,v\)) s tim event-čvorom. To je bipartitni graf: (originalni čvorovi) – (brid-čvorovi). Projekcija natrag na originalne čvorove vraća originalnu mrežu (u idealnom slučaju 1:1).
library(igraph)
library(dplyr)
gE <- g_giant
# 1) Jedinstveni ID čvora:
idE <- if ("name" %in% vertex_attr_names(gE)) V(gE)$name else as.character(seq_len(vcount(gE)))
# 2) Oznaka (smije biti duplicirana): Enron Name
labE <- if ("Name" %in% vertex_attr_names(gE)) V(gE)$Name else idE
labE[labE == "NA"] <- "Unknown" # "NA" je string, ne NA
# 3) Edgelist iz igrapha (ovdje from/to obično već odgovaraju V(gE)$name)
edE0 <- igraph::as_data_frame(gE, what = "edges") |>
transmute(from = as.character(from), to = as.character(to)) |>
mutate(e_id = paste0("E_", row_number()))
# Ako su from/to numerički indeksi, onda treba mapiranje na idE:
# edE0 <- edE0 |> mutate(from = idE[as.integer(from)], to = idE[as.integer(to)])
# 4) Bipartitne veze: P_<id> -- E_<edge>
bip_ed_E <- bind_rows(
transmute(edE0, from = paste0("P_", from), to = e_id),
transmute(edE0, from = paste0("P_", to), to = e_id)
)
# 5) Vertex tablica: "person" + "edge-node"
V_nodes <- data.frame(
name = paste0("P_", idE),
type = FALSE, # FALSE = person
label = labE, # labela smije biti duplicirana
stringsAsFactors = FALSE
)
V_edges <- data.frame(
name = unique(edE0$e_id),
type = TRUE, # TRUE = edge-node
stringsAsFactors = FALSE
)
V_bip_E <- bind_rows(V_nodes, V_edges)
stopifnot(anyDuplicated(V_bip_E$name) == 0)
# 6) Konstrukcija bipartitnog grafa
gE_bip <- graph_from_data_frame(bip_ed_E, directed = FALSE, vertices = V_bip_E)
gE_bip
## IGRAPH 840a5cd UN-B 3192 6020 --
## + attr: name (v/c), type (v/l), label (v/c)
## + edges from 840a5cd (vertex names):
## [1] P_1--E_1 P_1--E_2 P_1--E_3 P_1--E_4 P_1--E_5 P_1--E_6 P_2--E_7
## [8] P_2--E_8 P_2--E_9 P_2--E_10 P_2--E_11 P_2--E_12 P_2--E_13 P_2--E_14
## [15] P_2--E_15 P_2--E_16 P_2--E_17 P_3--E_18 P_3--E_19 P_3--E_20 P_3--E_21
## [22] P_3--E_22 P_3--E_23 P_3--E_24 P_3--E_25 P_3--E_26 P_3--E_27 P_3--E_28
## [29] P_4--E_29 P_4--E_30 P_4--E_31 P_5--E_32 P_5--E_33 P_5--E_34 P_5--E_35
## [36] P_5--E_36 P_5--E_37 P_5--E_38 P_5--E_39 P_5--E_40 P_5--E_41 P_5--E_42
## [43] P_5--E_43 P_5--E_44 P_5--E_45 P_5--E_46 P_5--E_47 P_5--E_48 P_5--E_49
## [50] P_5--E_50 P_5--E_51 P_5--E_52 P_5--E_53 P_5--E_54 P_5--E_55 P_5--E_56
## + ... omitted several edges
table(V(gE_bip)$type) # koliko "person" vs "edge-nodes"
##
## FALSE TRUE
## 182 3010
# 4) Projekcija natrag na "aktere" (type=FALSE)
projE <- bipartite_projection(gE_bip, which = "false") # "false" = originalni čvorovi
projE
## IGRAPH 840c6ef UNW- 182 2097 --
## + attr: name (v/c), label (v/c), weight (e/n)
## + edges from 840c6ef (vertex names):
## [1] P_1--P_10 P_1--P_21 P_1--P_49 P_1--P_91 P_1--P_104 P_1--P_151
## [7] P_1--P_105 P_2--P_16 P_2--P_40 P_2--P_44 P_2--P_69 P_2--P_71
## [13] P_2--P_75 P_2--P_82 P_2--P_88 P_2--P_107 P_2--P_156 P_2--P_158
## [19] P_2--P_5 P_2--P_7 P_2--P_30 P_2--P_78 P_2--P_85 P_2--P_89
## [25] P_2--P_90 P_2--P_94 P_2--P_105 P_2--P_125 P_2--P_152 P_3--P_11
## [31] P_3--P_18 P_3--P_44 P_3--P_51 P_3--P_88 P_3--P_138 P_3--P_143
## [37] P_3--P_153 P_3--P_155 P_3--P_156 P_3--P_165 P_3--P_60 P_3--P_78
## [43] P_3--P_82 P_3--P_84 P_3--P_90 P_3--P_105 P_3--P_129 P_3--P_144
## + ... omitted several edges
# 5) Provjera: broj bridova bi trebao odgovarati (može biti > ako postoje multi-bridovi)
c(
original_edges = ecount(gE),
back_edges = ecount(igraph::simplify(projE, remove.multiple = TRUE, remove.loops = TRUE))
)
## original_edges back_edges
## 3010 2097
# 6) Jedan pokazatelj: gustoća (pokazuje da se projekcijom ovdje ništa "ne napuhuje")
c(
dens_original = edge_density(as.undirected(igraph::simplify(gE))),
dens_back = edge_density(as.undirected(igraph::simplify(projE)))
)
## dens_original dens_back
## 0.1273147 0.1273147
napravili smo bipartitnu mrežu tipa osoba–brid (edge-nodes).
kad to projeciramo natrag na “osoba–osoba”, dobijemo vezu između dvije osobe ako dijele barem jedan edge-node.
u edge-node konstrukciji, svaka originalna veza (\(u,v\)) stvara jedan edge-node \(E_k\) koji je spojen baš s \(u\) i \(v\). Projekcija natrag zato “zna” samo da su \(u\) i \(v\) povezani — isto kao original.
Razlika u back_edges (2097 vs 3010) dolazi od ovoga:
originalni gE je usmjeren i može imati oba smjera
\(u→v\) i \(v→u\) kao dva različita brida.
Kad smo računali gustoću, koristili smo
as.undirected(simplify(...)), pa se ti parovi spajaju u
jednu neusmjerenu vezu.
Zato projekcija (koja po prirodi označava “barem jedan odnos
između \(u\) i \(v\)”) završi s istim skupom neusmjerenih
parova kao i as.undirected(simplify(gE)).
Drugim riječima: edge-node dual je u ovom smislu gotovo “bez gubitka” kad se vratimo na neusmjereni simple graf.
library(igraph)
library(dplyr)
gU <- g_weak_giant
edU <- igraph::as_data_frame(gU, what = "edges") |>
transmute(from = as.character(from),
to = as.character(to),
e_id = paste0("e", row_number()))
V_nodes <- data.frame(name = V(gU)$name, type = FALSE)
V_edges <- data.frame(name = edU$e_id, type = TRUE)
V_bip_U <- bind_rows(V_nodes, V_edges)
bip_ed_U <- bind_rows(
transmute(edU, from = from, to = e_id),
transmute(edU, from = to, to = e_id)
)
gU_bip <- graph_from_data_frame(bip_ed_U, directed = FALSE, vertices = V_bip_U)
projU <- bipartite_projection(gU_bip, which = "false")
gU_back <- projU
c(
original_edges = ecount(gU),
back_edges = ecount(igraph::simplify(gU_back, remove.multiple = TRUE, remove.loops = TRUE))
)
## original_edges back_edges
## 3148 1697
c(
dens_original = edge_density(as.undirected(igraph::simplify(gU))),
dens_back = edge_density(as.undirected(igraph::simplify(gU_back)))
)
## dens_original dens_back
## 0.02876466 0.02876466
Zašto su gustoće iste, ali je back_edges manji?
Naš edge-node bipartitni graf kodira svaki originalni brid
kao jedan “event čvor” e_id, spojen s oba kraja brida
(from—e_id i to—e_id). Kad projeciramo to
natrag na osobe, dvije osobe dobiju vezu ako dijele barem jedan
edge-node. To u biti znači da u projekciji postoji veza između
\(u\) i \(v\) ⇔ u originalu je postojao barem jedan
brid između \(u\) i \(v\) (u bilo kojem smjeru).
gU je usmjeren i može imati oba smjera \(u→v\) i \(v→u\) kao dvije različite veze (a često i
višestruke po paru). Projekcija iz edge-node konstrukcije daje
(praktično) neusmjeren “simple” odnos “\(u\) i \(v\) su povezani barem jednom” i zbog toga
dobivamo razliku u broju bridova. Odnosno, kad se radi “edge-node”
bipartitizacija i povratna projekcija, koristimo transformaciju koja, uz
simplify() i uklanjanje multiple/loops, može kolabirati
multiplicitete i promijeniti broj bridova, ali gustoća može ostati ista
jer se mijenja i broj mogućih bridova (ili se rezultat dodatno
pojednostavnjuje).
Ovo je dualnost “čvor–veza” (incidencija). Ne mijenja semantiku relacije; pokazuje da je i “brid” također objekt koji se može modelirati kao čvor, ovisno o istraživačkom pitanju.
Provjera (usporedba s usmjerenom i neusmjerenom varijantom
gU):
gU_simple_undir <- as.undirected(
igraph::simplify(gU, remove.multiple = TRUE, remove.loops = TRUE),
mode = "collapse"
)
gU_back_simple_undir <- as.undirected(
igraph::simplify(gU_back, remove.multiple = TRUE, remove.loops = TRUE),
mode = "collapse"
)
c(
e_original_directed = ecount(gU),
e_original_undir = ecount(gU_simple_undir),
e_back_undir = ecount(gU_back_simple_undir)
)
## e_original_directed e_original_undir e_back_undir
## 3148 1697 1697
c(
dens_original_undir = edge_density(gU_simple_undir),
dens_back_undir = edge_density(gU_back_simple_undir)
)
## dens_original_undir dens_back_undir
## 0.02876466 0.02876466
Ova provjera potvrđuje prethodno dano objašnjenje razlike u broju bridova.
Bipartitna (osoba–kolegij) → jednopartitne projekcije (osobe i
kolegiji) - Breiger: (\(A = B B^T\)),
(\(C = B^T B\)); prirodno za UNIPU jer
imamo predmet. Ovdje se lijepo vidi i “distorzija
projekcije”: jedan kolegij s puno izvođača stvara “klik” u projekciji
osoba.
Iz predmet (atribut brida) konstruirati ćemo veze
osoba–kolegij.
library(igraph)
library(dplyr)
gU <- g_weak_giant
# 1) Iz bridova izvučemo "tko sudjeluje na kojem kolegiju"
# (ako brid predstavlja odnos na kolegiju, oba kraja su sudionici tog kolegija)
edU <- igraph::as_data_frame(gU, what = "edges") |>
transmute(from = as.character(from),
to = as.character(to),
predmet = as.character(predmet))
# parovi (osoba, kolegij) iz oba kraja brida
affU <- bind_rows(
transmute(edU, osoba = from, kolegij = predmet),
transmute(edU, osoba = to, kolegij = predmet)
) |>
filter(!is.na(kolegij), kolegij != "") |>
distinct()
# 2) Bipartitni graf osoba–kolegij
V_osoba <- data.frame(name = sort(unique(affU$osoba)), type = FALSE)
V_kolegij <- data.frame(name = sort(unique(affU$kolegij)), type = TRUE)
V_bip <- bind_rows(V_osoba, V_kolegij)
gU_aff <- graph_from_data_frame(
d = transmute(affU, from = osoba, to = kolegij),
directed = FALSE,
vertices = V_bip
)
# 3) Projekcije: osobe i kolegiji
proj <- bipartite_projection(gU_aff)
gU_person <- proj$proj1 # (ovisno o verziji igraph-a, ponekad je proj1 = type FALSE)
gU_course <- proj$proj2
# sigurnije: eksplicitno
gU_person <- bipartite_projection(gU_aff, which = "false")
gU_course <- bipartite_projection(gU_aff, which = "true")
# 4) Pokazatelji koji demonstriraju "napuhavanje" projekcije:
# gustoća i prosječni stupanj
out <- rbind(
data.frame(mreza = "bipartitna (osoba-kolegij)",
n = vcount(gU_aff), m = ecount(gU_aff),
density = igraph::edge_density(gU_aff),
k_avg = mean(igraph::degree(gU_aff))),
data.frame(mreza = "projekcija osobe-osobe",
n = vcount(gU_person), m = ecount(gU_person),
density = igraph::edge_density(igraph::simplify(gU_person)),
k_avg = mean(igraph::degree(igraph::simplify(gU_person)))),
data.frame(mreza = "projekcija kolegij-kolegij",
n = vcount(gU_course), m = ecount(gU_course),
density = igraph::edge_density(igraph::simplify(gU_course)),
k_avg = mean(igraph::degree(igraph::simplify(gU_course))))
)
print(out)
## mreza n m density k_avg
## 1 bipartitna (osoba-kolegij) 668 996 0.004470818 2.982036
## 2 projekcija osobe-osobe 344 3801 0.064428097 22.098837
## 3 projekcija kolegij-kolegij 324 1645 0.031437526 10.154321
n = 668 čvorova = 344 osobe + 324 kolegija
m = 996 afilijacijskih veza (osoba je vezana na kolegij)
gustoća = 0.0045 (očekivano vrlo rijetka)
prosječni stupanj = 2.98
U bipartitnom grafu to znači: prosječan čvor (osoba ili kolegij) ima oko 3 veze. (Nije isto kao “prosječan broj kolegija po osobi”, jer se prosjek računa preko oba tipa čvorova.)
n = 344 (samo osobe)
m = 3801, gustoća = 0.064, k_avg = 22.10
dvije osobe su povezane ako dijele barem jedan kolegij → to često “napuhuje” broj veza.
Prosječno je osoba povezana s ~22 druge osobe u projekciji, što je puno “gušće” iskustvo nego u originalnoj bipartitnoj reprezentaciji.
n = 324 (samo kolegiji)
m = 1645, gustoća = 0.031, k_avg = 10.15
dva kolegija su povezana ako dijele barem jednu osobu → otkriva “klastere kurikuluma” (kolegiji koji dijele nastavnike/izvođače), ali opet projekcija proizvodi dodatne veze.
Bipartitna mreža nosi “izvornu” strukturu (tko sudjeluje u čemu), a jednopartitne projekcije su model koji dodaje relacije “dijele X”, što mijenja gustoću, stupnjeve i kasnije pozicije/uloge.
# za prikaz je dobro pojednostaviti projekcije (maknuti loopove i multiple)
g_aff <- gU_aff
g_pp <- igraph::simplify(gU_person, remove.multiple = TRUE, remove.loops = TRUE)
g_cc <- igraph::simplify(gU_course, remove.multiple = TRUE, remove.loops = TRUE)
# layouti (odvojeno, jer grafovi imaju različite skupove čvorova)
set.seed(1)
lay_aff <- layout_with_fr(g_aff)
lay_pp <- layout_with_fr(g_pp)
lay_cc <- layout_with_fr(g_cc)
# boje: bipartitno obojimo po tipu čvora, projekcije jednobojno
col_aff <- ifelse(V(g_aff)$type, "grey60", "red") # TRUE=kolegij, FALSE=osoba
# crtanje
par(mfrow = c(1, 3), mar = c(1, 1, 3, 1))
plot(
g_aff, layout = lay_aff,
vertex.color = col_aff,
vertex.size = 3,
vertex.label = NA,
edge.color = "grey80",
main = paste0("Bipartitna (osoba–kolegij)\n",
"n=", vcount(g_aff), ", m=", ecount(g_aff),
", dens=", round(edge_density(g_aff), 4))
)
plot(
g_pp, layout = lay_pp,
vertex.color = "grey60",
vertex.size = 3,
vertex.label = NA,
edge.color = "grey80",
main = paste0("Projekcija osoba–osoba\n",
"n=", vcount(g_pp), ", m=", ecount(g_pp),
", dens=", round(edge_density(g_pp), 4))
)
plot(
g_cc, layout = lay_cc,
vertex.color = "grey60",
vertex.size = 3,
vertex.label = NA,
edge.color = "grey80",
main = paste0("Projekcija kolegij–kolegij\n",
"n=", vcount(g_cc), ", m=", ecount(g_cc),
", dens=", round(edge_density(g_cc), 4))
)
U nastavku demonstriramo kako odluka o granici mreže (boundary problem) izravno mijenja dobivenu strukturu. U dvomodalnoj mreži osoba–kolegij, projekcija na mrežu osoba–osoba može postati umjetno gusta ako uključimo kolegije s velikim brojem sudionika (jedan “veliki” kolegij može povezati velik dio ljudi u kliku). Zato uvodimo jednostavno, ilustrativno pravilo: izbacujemo kolegije iznad odabranog praga broja sudionika i ponovno gradimo bipartitnu mrežu te projekciju. Usporedbom gustoće projekcije prije i poslije filtriranja vidimo da promjena granice (koje aktivnosti računamo kao relevantne) mijenja gustoću, potencijalne klastere i posljedično i interpretaciju “pozicija”.
library(dplyr)
library(igraph)
# broj sudionika po kolegiju
course_size <- affU |> count(kolegij, name = "n_osoba")
# npr. izbaciti kolegije s > 12 sudionika (prag je ilustrativni, ne "istina")
affU_small <- affU |>
left_join(course_size, by = "kolegij") |>
filter(n_osoba <= 12) |>
select(osoba, kolegij)
gU_aff_small <- graph_from_data_frame(
transmute(affU_small, from = osoba, to = kolegij),
directed = FALSE,
vertices = V_bip
)
gU_person_small <- bipartite_projection(gU_aff_small, which = "false")
c(
dens_person_all = edge_density(igraph::simplify(gU_person)),
dens_person_small = edge_density(igraph::simplify(gU_person_small))
)
## dens_person_all dens_person_small
## 0.06442810 0.01225507
Promjenom granice (što ulazi kao “aktivnost” i koje aktivnosti se smatraju relevantnima) mijenjaju se gustoća, klasteri i time i “pozicije”.
Pozicija: Strukturno mjesto čvora u mreži (obrazac veza).
Uloga: Funkcija/ponašanje čvora proizlazi iz njegove pozicije.
Strukturna ekvivalencija: Čvorovi povezani s istim drugima na sličan način.
Regularna ekvivalencija: Čvorovi imaju slične veze prema sličnim tipovima čvorova.
Automorfna ekvivalencija: Čvorovi su automorfno ekvivalentni ako ih je moguće međusobno zamijeniti bez promjene ukupne strukture mreže (globalna topološka simetrija).
Orbita (u automorfnoj ekvivalenciji): Skup čvorova koji su međusobno zamjenjivi u okviru iste automorfne simetrije grafa.
Makro-arhitektura: Obrazac odnosa među pozicijama (sažetak strukture na razini blokova).
Blok-modeliranje: Grupiranje čvorova u blokove prema ekvivalenciji/uzorcima veza.
Matrica slike (image matrix): Sažeta matrica gustoća između pozicija (blokmodel na razini klasa).
Dualnost čvor–veza: Čvorovi i veze se mogu modelirati međusobno (npr. događaji).
Hijerarhija (mrežna): Obrazac odnosa u kojem su veze usmjerene od “viših” prema “nižim” razinama, uz minimalnu recipročnost i odsutnost ciklusa.
Usmjerena aciklička mreža (DAG): Usmjerena mreža bez ciklusa, u kojoj nije moguće krenuti od čvora i vratiti se u njega slijedeći smjer veza.
Jaka povezana komponenta (SCC): Skup čvorova u usmjerenoj mreži u kojem je svaki čvor dostižan iz svakog drugog čvora putem usmjerenih putova.
Kondenzacija mreže: Postupak u kojem se svaka jaka povezana komponenta zamjenjuje jednim “super-čvorom”, čime nastaje DAG među tim komponentama.
Dominacija: Asimetričan odnos moći/utjecaja (npr. A kontrolira B).
Podređenost: Suprotno dominaciji; pozicija niže moći u odnosu.
Centar–periferija (pozicijski): Pozicije definirane blizinom jezgre i uzorkom veza.
Autoritet: Čvor prepoznat kao izvor pouzdanih/važnih informacija.
Prestiž: Status čvora često mjerljiv primanjem veza (indegree).
Status: Socijalna vrijednost/pozicija u sustavu odnosa.
Formalne uloge: Uloge definirane pravilima (npr. menadžer).
Neformalne uloge: Uloge nastale kroz praksu (mentor, posrednik).
Organizacijska struktura: Formalna raspodjela funkcija i odgovornosti.
Mrežna hijerarhija: Hijerarhijski obrazac vidljiv u odnosima, ne nužno formalno.
DAG (usmjereni aciklički graf): Struktura bez ciklusa; ideal hijerarhije.
Outtree: Minimalna hijerarhijska struktura s jednim korijenom (m = n − 1).
Vertikalne veze: Veze između razina (nadređeni–podređeni).
Horizontalne veze: Veze među istom razinom (kolege).
Prijelaz faze (Phase transition): Kritična točka gustoće pri kojoj nastaje gigantska komponenta i omogućuje diferencijaciju pozicija.
Funkcionalna diferencijacija: Različite funkcije/uloge u podsustavima.
Gould–Fernandez posredovanje (brokerage): Klasifikacija uloga aktera u trijadama i→b→j ovisno o pripadnosti skupinama (koordinator, gatekeeper, representative, liaison).
Uloge mostova: Čvorovi koji spajaju dijelove mreže.
Gatekeeper: Čvor koji kontrolira ulaz/izlaz informacija prema grupi.
Koordinator (Coordinator): Posreduje unutar vlastite grupe.
Representative (Predstavnik): Prenosi informacije iz svoje grupe prema drugoj.
Liaison: Posreduje između dviju grupa kojima ne pripada.
Brokerage: Posredovanje u trijadi i → b → j ovisno o grupama.
Projekcija (one-mode projection): Transformacija bipartitne mreže u jednopartitnu mrežu u kojoj su čvorovi povezani ako dijele zajednički entitet iz druge skupine.
Barabasi, A. L. (2022). Network Science. Cambridge University Pr.(2016). URL: https://www. ebook. de/de/product/24312547/albert_laszlo_barabasi_network_science. html.
Barabási, A. L., & Albert, R. (1999). Emergence of scaling in random networks. science, 286(5439), 509-512.
Bojanowski, M. (2022). intergraph: Coercion routines for network data objects (R package). Comprehensive R Archive Network (CRAN). https://CRAN.R-project.org/package=intergraph
Borgatti, S. P., & Everett, M. G. (2006). A graph-theoretic perspective on centrality. Social networks, 28(4), 466-484.
Breiger, R. L. (1974). The duality of persons and groups. Social forces, 53(2), 181-190.
Burris, V. (2004). The academic caste system: Prestige hierarchies in PhD exchange networks. American sociological review, 69(2), 239-264.
Butts, C. T. (2008). Social network analysis with sna. Journal of statistical software, 24, 1-51.
Butts, C. T. (2023). network: Classes for relational data (R package). Comprehensive R Archive Network (CRAN). https://CRAN.R-project.org/package=network
Butts, C. T. (2023). sna: Tools for social network analysis (R package). Comprehensive R Archive Network (CRAN). https://CRAN.R-project.org/package=sna
Csardi, G., & Nepusz, T. (2006). The igraph software. Complex syst, 1695, 1-9.
Gould, R. V. (2003). Collision of wills: How ambiguity about social rank breeds conflict. University of Chicago Press.
Jackson, M. O. (2008). Social and Economic Networks. Princeton University Press.
Jurca, G., Addam, O., Rokne, J., & Alhajj, R. (2018). Social network based big data analysis and applications. In M. Kaya et al. (Eds.), Lecture notes in social networks. Springer.
Kleinberg, J. M. (1999). Authoritative sources in a hyperlinked environment. Journal of the ACM (JACM), 46(5), 604-632.
Knoke, D., & Yang, S. (2019). Social Network Analysis (3. izd.). SAGE Publications.
Kostelić, K., & Turk, M. (2021). Topology of the world tourism web. Applied Sciences, 11(5), 2253.
Krackhardt, D. (2014). Graph theoretical dimensions of informal organizations. U K. M. Carley & M. J. Prietula (Ur.), Computational Organization Theory (str. 107–130). Psychology Press.
Lamba, M., & Madhusudhan, M. (2022). Text mining for information professionals. An Uncharted Territoriy. Cham.
McFarland, D. A. (2001). Student resistance: How the formal and informal organization of classrooms facilitate everyday forms of student defiance. American journal of Sociology, 107(3), 612-678.
Nobari, S., Lu, X., Karras, P., & Bressan, S. (2011, March). Fast random graph generation. In Proceedings of the 14th international conference on extending database technology (pp. 331-342).
Ooms, J. (2023). gifski: Highest quality GIF encoder (R package). Comprehensive R Archive Network (CRAN). https://CRAN.R-project.org/package=gifski
Padgett, J. F., & Ansell, C. K. (1993). Robust Action and the Rise of the Medici, 1400-1434. American journal of sociology, 98(6), 1259-1319.
Rawlings, C. M., Smith, J. A., Moody, J., & McFarland, D. A. (2023). Network analysis: integrating social network theory, method, and application with R. Cambridge University Press.
Van Steen, M. (2010). Graph Theory and Complex Networks: An Introduction.
Wasserman, S., & Faust, K. (1994). Social network analysis: Methods and applications.
Wickham, H., François, R., Henry, L., & Müller, K. (2023). dplyr: A grammar of data manipulation (R package). Comprehensive R Archive Network (CRAN). https://CRAN.R-project.org/package=dplyr
b
b
b
b
b
c
a
b
c
b
c
c
b
a
b
b
b
a
b
b
b
a
b
b
a
b
a
a
a
a
b
b
b
a
b
a
a
a
a
a
sessionInfo()
## R version 4.4.2 (2024-10-31 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 10 x64 (build 19045)
##
## Matrix products: default
##
##
## locale:
## [1] LC_COLLATE=Croatian_Croatia.utf8 LC_CTYPE=Croatian_Croatia.utf8
## [3] LC_MONETARY=Croatian_Croatia.utf8 LC_NUMERIC=C
## [5] LC_TIME=Croatian_Croatia.utf8
##
## time zone: Europe/Zagreb
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices datasets utils methods base
##
## other attached packages:
## [1] intergraph_2.0-4 ggalluvial_0.12.6 tidyr_1.3.2
## [4] purrr_1.2.1 stringr_1.6.0 dplyr_1.2.0
## [7] igraphdata_1.0.1 sna_2.8 network_1.20.0
## [10] statnet.common_4.13.0 ggraph_2.2.2 ggplot2_4.0.2
## [13] tidygraph_1.3.1 igraph_2.2.1
##
## loaded via a namespace (and not attached):
## [1] viridis_0.6.5 utf8_1.2.6 sass_0.4.10 generics_0.1.4
## [5] renv_1.1.1 stringi_1.8.7 lattice_0.22-6 digest_0.6.39
## [9] magrittr_2.0.4 evaluate_1.0.5 grid_4.4.2 RColorBrewer_1.1-3
## [13] fastmap_1.2.0 jsonlite_2.0.0 ggrepel_0.9.6 gridExtra_2.3
## [17] viridisLite_0.4.3 scales_1.4.0 tweenr_2.0.3 jquerylib_0.1.4
## [21] cli_3.6.5 rlang_1.1.7 graphlayouts_1.2.2 polyclip_1.10-7
## [25] withr_3.0.2 cachem_1.1.0 yaml_2.3.12 tools_4.4.2
## [29] memoise_2.0.1 coda_0.19-4.1 vctrs_0.7.1 R6_2.6.1
## [33] lifecycle_1.0.5 MASS_7.3-61 pkgconfig_2.0.3 pillar_1.11.1
## [37] bslib_0.10.0 gtable_0.3.6 glue_1.8.0 Rcpp_1.1.1
## [41] ggforce_0.5.0 xfun_0.56 tibble_3.3.1 tidyselect_1.2.1
## [45] rstudioapi_0.18.0 knitr_1.51 farver_2.1.2 htmltools_0.5.9
## [49] labeling_0.4.3 rmarkdown_2.30 compiler_4.4.2 S7_0.2.1