Deeper Dives
EGA is the core function of the package: it estimates a
network from your data and applies a community detection algorithm to
recover the number of dimensions. This workflow looks at just that one
step in isolation, and at a choice that’s easy to overlook — which
community detection algorithm to use — using the Total Entropy Fit Index
(tefi) to compare the result.
We’ll use the wmt2 dataset (introduced in Quick Start), a matrix reasoning
test with a well-established two-dimensional structure.
# Load {EGAnet}
library(EGAnet)
# Obtain data
wmt <- wmt2[,7:24]EGA’s default community detection algorithm is
"walktrap", a random-walk method that tends to produce
compact, conservative communities:
ega_walktrap <- EGA(
data = wmt, algorithm = "walktrap",
plot.EGA = FALSE # No plot for CRAN checks
)
# Print results
ega_walktrapModel: GLASSO (EBIC with gamma = 0.5)
Correlations: auto
Lambda: 0.0648641196433721 (n = 100, ratio = 0.1)
Number of nodes: 18
Number of edges: 96
Edge density: 0.627
Non-zero edge weights:
M SD Min Max
0.082 0.060 -0.013 0.363
----
Algorithm: Walktrap
Number of communities: 2
wmt1 wmt2 wmt3 wmt4 wmt5 wmt6 wmt7 wmt8 wmt9 wmt10 wmt11 wmt12 wmt13
1 1 1 1 1 2 2 2 2 2 2 2 2
wmt14 wmt15 wmt16 wmt17 wmt18
2 2 2 2 2
----
Unidimensional Method: Louvain
Unidimensional: No
----
TEFI: -11.171
community.detection supports several other
igraph algorithms passed along through EGA’s
algorithm argument — among them "spinglass"
(Reichardt &
Bornholdt, 2006), a method based on statistical mechanics that can
be more sensitive to smaller, weakly-separated clusters:
ega_spinglass <- EGA(
data = wmt, algorithm = "spinglass",
plot.EGA = FALSE # No plot for CRAN checks
)
# Print results
ega_spinglassModel: GLASSO (EBIC with gamma = 0.5)
Correlations: auto
Lambda: 0.0648641196433721 (n = 100, ratio = 0.1)
Number of nodes: 18
Number of edges: 96
Edge density: 0.627
Non-zero edge weights:
M SD Min Max
0.082 0.060 -0.013 0.363
----
Algorithm: Spinglass
Number of communities: 3
wmt1 wmt2 wmt3 wmt4 wmt5 wmt6 wmt7 wmt8 wmt9 wmt10 wmt11 wmt12 wmt13
1 1 1 1 1 2 2 2 3 3 2 2 3
wmt14 wmt15 wmt16 wmt17 wmt18
3 2 3 2 2
----
Unidimensional Method: Louvain
Unidimensional: No
----
TEFI: -10.932
Walktrap recovers two dimensions; spinglass splits the same items
into four. Rather than argue over which looks right, we can
compare how well each structure fits the data using tefi —
lower (more negative) values indicate better fit:
# Walktrap TEFI
ega_walktrap$TEFI[1] -11.17103
# Spinglass TEFI
ega_spinglass$TEFI[1] -10.93215
Walktrap’s two-dimension solution has the lower TEFI (-11.17
vs. -10.93), meaning it’s the better-fitting structure of the two —
despite spinglass finding more dimensions. This is a useful
reminder: a community detection algorithm that fractures a network into
additional communities isn’t automatically finding a more meaningful
structure. More dimensions only help if they earn their keep in fit,
which is exactly what tefi is for.
In practice, this is why EGA’s default pairs
"walktrap" with a uni.method that itself
defaults to "louvain" for the unidimensionality check — and
why it’s worth comparing algorithms with tefi rather than
assuming the default is always right for your data. When two algorithms
disagree, tefi (or, across many structures at once,
genTEFI for hierarchical/multi-level comparisons) gives you
a principled way to adjudicate.
That uni.method = "louvain" default is worth a closer
look, because Louvain isn’t a deterministic algorithm: it processes
nodes in a randomly shuffled order, and different orderings can settle
into different communities. Called on its own, with no seed,
community.unidimensional — the function behind
EGA’s unidimensionality check — can genuinely disagree with
itself from one call to the next. A single call won’t reliably show this
(it might have to get unlucky to disagree), so here it is over 30 calls
on the exact same data:
# Same data, no seed, thirty calls
table(sapply(1:30, function(i) community.unidimensional(wmt, verbose = FALSE)["wmt6"]))
1 2
9 21
wmt6 sits right on the boundary between the two
empirical dimensions (it’s the first item of the second), so it’s
exactly the kind of item a randomized node order can push back and forth
— landing in one dimension some of the time and the other the rest, with
nothing in the call itself to say which you’ll get. This isn’t a
one-off: it’s the same item the bootEGA
workflow independently flags as the least stable in the whole
scale.
{EGAnet}’s own community detection routines are seeded
internally with a reproducible generator (xoshiro256++, since version
2.5.0), and EGA and bootEGA both expose that
through a seed argument. Setting one fixes the random node
order — and everything downstream of it — for the entire call:
# Same data, same seed, six calls in a row
sapply(1:6, function(i) {
EGA(wmt, algorithm = "louvain", plot.EGA = FALSE, verbose = FALSE, seed = 1)$n.dim
})[1] 3 3 3 3 3 3
Every call agrees. If you need your own analysis — or someone else’s
replication of it — to land on the exact same structure every time,
seed is what guarantees that. It matters even more once
you’re not running one EGA, but hundreds of them at once,
which is exactly what bootEGA does; see the bootEGA workflow for what happens without
it.