Plotting in {EGAnet} has become much more flexible by passing on
most arguments to {GGally}’s ggnet2 (some arguments might
not change to keep a consistent {EGAnet} style to the plots). This Wiki
walks through some of the flexibility available in {EGAnet}’s plots.
This demonstration is only the start – you can use these plots as the
foundation for your own creations. These examples focus solely on
EGA plotting but apply to all *EGA plots.
The sections below start with the basics — getting a plot on screen
and tweaking the obvious things (color, size, labels) — then move into
more advanced usage that leans on how {EGAnet} builds its plots on top
of ggnet2 internally: reusing a layout across plots, sizing
nodes correctly by community, and a couple of genuine gotchas worth
knowing about before you hit them.
# Load necessary packages
library(EGAnet); library(GGally); library(ggplot2)
# Estimate EGA
ega.wmt <- EGA(wmt2[,7:24], plot.EGA = FALSE)
# Plot
plot(ega.wmt)
plot(ega.wmt, legend.names = c("Dimension 1", "Dimension 2"))
plot(ega.wmt) + theme(legend.position = "none")
Handling legend title and text can be done using the standard
?ggplot2::theme arguments
plot(ega.wmt) + ggtitle("EGA Title")
plot(ega.wmt) +
ggtitle("Better EGA Title") +
theme(plot.title = element_text(size = 14, face = "bold", hjust = 0.5))
For all available layout options, see
?sna::gplot.layout. Remove gplot.layout. to
use the layout (e.g., gplot.layout.circle =
layout = "circle")
plot(ega.wmt, layout = "circle")
plot(ega.wmt, layout = "eigen")
Change Colors Using One of {EGAnet}’s Own Palettes (see
?color_palette_EGA for the full list —
"polychrome", "blue.ridge1",
"blue.ridge2", "rio", "itacare",
"grayscale", and "rainbow")
plot(ega.wmt, color.palette = "blue.ridge2")
Change Colors Using an {RColorBrewer} Palette (any
palette name from RColorBrewer::brewer.pal.info works)
plot(ega.wmt, color.palette = "Set2")
Change Colors Using HEX Codes (one per dimension — handy for matching institutional or journal color schemes)
plot(ega.wmt, node.color = c("#F5815A", "#47BCC9"))
node.color is the one node argument {EGAnet} expands
from “one value per community” to “one value per node” automatically
(it’s what makes the line above work with only 2 hex codes for 18
nodes). Most other node arguments don’t get this treatment — see Sizing and Shaping Nodes
by Community in the advanced section below.
For quick color palette ideas, check out this color palette generator
plot(ega.wmt, node.size = 12)
Change Size based on Node Strength
plot(ega.wmt, node.size = colSums(ega.wmt$network)^2 * 16)
plot(ega.wmt, node.alpha = 0.5)
By default, nodes are labeled with their column names, but any character vector of the same length can be used instead — useful for swapping in full item wording or a different item numbering scheme:
plot(ega.wmt, label = paste0("Item ", 1:18))
plot(ega.wmt, label = FALSE)
plot(ega.wmt, label.size = 6, label.color = "white")
plot(ega.wmt, edge.size = 12)
plot(ega.wmt, edge.alpha = 0.1)
plot(
ega.wmt, edge.alpha = 1,
edge.lty = c( # line type
"dashed", # positive edge first
"solid" # negative edge second
),
edge.color = c( # color
"pink", # positive edges first
"black" # negative edges second
)
)
plot(
ega.wmt,
edge.color = c(
"darkgreen", # positive edges (default color)
"white" # negative edges second
)
)
plot(
ega.wmt, edge.alpha = 1, # used to increase visibility
edge.color = c(
"white", # positive edges first
"red" # negative edges (default color)
)
)
Every argument above is just one more
ggnet2/ggplot2 layer, so they compose freely
into a single call:
plot(
ega.wmt,
color.palette = "itacare",
node.size = 14, node.alpha = 0.9,
label.size = 4, label.color = "white",
edge.alpha = 0.6
) +
ggtitle("WMT-2 Structure") +
theme(plot.title = element_text(hjust = 0.5, face = "bold"))
Every plot above is a ggplot2 object, so
ggsave handles exporting it — to PNG, PDF, SVG, or anything
else it supports — at whatever size and resolution you need for a
manuscript or slide deck:
my_plot <- plot(ega.wmt, color.palette = "blue.ridge2")
ggsave("wmt-structure.png", my_plot, width = 6, height = 5, dpi = 300)The sections below dig into how {EGAnet} builds a plot on top of
ggnet2 under the hood (see
EGAnet:::basic_plot_setup and
EGAnet:::GGally_args, if you want to read the source).
Knowing this saves real debugging time — a couple of arguments look like
they should work by analogy with node.color and quietly
don’t.
Every plot recomputes its node layout from scratch, so plotting the same variables twice — once with each color palette, say — can land the nodes in two different arrangements, which makes the two plots hard to compare side by side.
Passing the hidden arguments = TRUE argument returns the
plot and the exact argument list {EGAnet} handed to
ggnet2, including the node coordinate matrix it computed
(mode):
built <- plot(ega.wmt, arguments = TRUE)
# One row per node, one column per plotting dimension
layout_matrix <- built$ARGS$mode
head(layout_matrix) [,1] [,2]
[1,] 20.80234 16.56530
[2,] 16.83077 12.80324
[3,] 14.35195 9.68264
[4,] 17.21777 19.35513
[5,] 11.17200 13.27421
[6,] 10.00738 17.79397
Feeding that matrix back in as mode locks a second plot
to the same node positions, no matter what else changes:
plot(ega.wmt, mode = layout_matrix, color.palette = "blue.ridge2")
This is exactly what compare.EGA.plots does
internally to keep two structures visually aligned. Saving a layout
matrix by hand is worth doing any time you want multiple figures (in the
same paper, say) to place the same variables in the same spots.
By default there’s also some breathing room built into every plot’s
axes (layout.exp = 0.20) so long node labels don’t get
clipped at the edge. If labels are still clipped — long item wording,
for example — increase it:
plot(ega.wmt, label = paste0("A Longer Item Label ", 1:18), layout.exp = 0.45)
layout.par (arguments passed straight to the underlying
sna::gplot.layout.* function,
e.g. list(niter = 500) to run more Fruchterman-Reingold
iterations) only has an effect once
layout/mode is set to one of
sna’s layouts — {EGAnet}’s default
(mode = "qgraph") uses {qgraph}’s own layout
algorithm and ignores layout.par entirely:
plot(ega.wmt, layout = "fruchtermanreingold", layout.par = list(niter = 500))
As shown in Nodes > Colors above,
node.color accepts one value per community and {EGAnet}
expands it to the full node vector for you. node.size,
node.shape, and node.alpha do
not get the same treatment — a community-length vector
for any of them will build without complaint but throws an error the
moment the plot is actually drawn (printed, knitted, or saved), because
ggnet2 receives a vector that’s neither length 1 nor
length-18:
# This builds without error but fails when it's rendered:
# Aesthetics must be either length 1 or the same as the data (18).
plot(ega.wmt, node.size = c(8, 20))Index by the membership vector yourself to get the per-node vector
ggnet2 expects:
plot(ega.wmt, node.size = c(8, 20)[ega.wmt$wc])
node.shape has a subtler issue: {EGAnet} validates it
(so a bad length or type still errors), but the actual node markers are
hard-coded two layers deep — a solid circle (shape = 19)
drawn on top of an open-circle border (shape = 1) —
regardless of what node.shape is set to. It’s accepted for
compatibility with ggnet2’s own argument checking, but it
currently has no visible effect on an {EGAnet}
plot:
# Renders identically to plot(ega.wmt) -- node.shape is a no-op here
plot(ega.wmt, node.shape = rep(c(15, 17), length.out = 18))
color.palette = "colorblind" (an alias for
"grayscale") does more than desaturate the nodes — it also
switches the positive/negative edges to a second, redundant encoding
(solid grey vs. dashed dark grey) so the two edge types stay
distinguishable without relying on color at all:
plot(ega.wmt, color.palette = "colorblind")
Setting edge.color/edge.lty explicitly (as
in Edges > Colors and Line Type)
overrides this automatic behavior, so it’s an either/or: let
"colorblind" pick a coordinated grayscale + line-type
scheme, or take full manual control.
legend.position and legend.size are plain
ggnet2 arguments that pass straight through, useful when
the default right-hand legend crowds a wide network:
plot(ega.wmt, legend.position = "bottom", legend.size = 12)
compare.EGA.plots (used elsewhere to check bootEGA stability) is itself a
plotting function, and takes the same kind of labeling arguments to keep
multiple structures readable side by side:
ega.wmt.spinglass <- EGA(wmt2[,7:24], algorithm = "spinglass", plot.EGA = FALSE)
comparison <- compare.EGA.plots(
ega.wmt, ega.wmt.spinglass,
labels = c("Walktrap", "Spinglass")
)
Every *EGA result plots through the same
ggnet2-based machinery covered above, but a few objects add
their own specialized arguments on top:
| Object | Plot arguments | Full example |
|---|---|---|
hierEGA |
plot.type (“multilevel” or “separate”),
color.match (border lower-order nodes in their higher-order
factor’s color) |
Hierarchical EGA workflow |
invariance |
p_type (“p” or “p_BH”), p_value |
Measurement Invariance workflow |
dynEGA |
base (which plot’s layout to use as the reference),
id (which individual(s) to plot, for
level = "individual") |
Dynamic EGA workflow |
All three still accept every ggnet2 argument from the
sections above (color.palette, node.size,
edge.alpha, and so on) — the arguments in the table just
layer additional structure-specific control on top.