Plotting

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.

Basic

Basic Plot

# Load necessary packages
library(EGAnet); library(GGally); library(ggplot2)

# Estimate EGA
ega.wmt <- EGA(wmt2[,7:24], plot.EGA = FALSE)

# Plot
plot(ega.wmt)

Legend

Change Names

plot(ega.wmt, legend.names = c("Dimension 1", "Dimension 2"))

Remove legend

plot(ega.wmt) + theme(legend.position = "none")

Handling legend title and text can be done using the standard ?ggplot2::theme arguments

Title

Basic Title

plot(ega.wmt) + ggtitle("EGA Title")

Adjusted Title (centered, larger size, bold)

plot(ega.wmt) +
  ggtitle("Better EGA Title") +
  theme(plot.title = element_text(size = 14, face = "bold", hjust = 0.5))

Layout

For all available layout options, see ?sna::gplot.layout. Remove gplot.layout. to use the layout (e.g., gplot.layout.circle = layout = "circle")

Circle

plot(ega.wmt, layout = "circle")

Eigen

plot(ega.wmt, layout = "eigen")

Nodes

Colors

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

Size

plot(ega.wmt, node.size = 12)

Change Size based on Node Strength

plot(ega.wmt, node.size = colSums(ega.wmt$network)^2 * 16)

Transparency

plot(ega.wmt, node.alpha = 0.5)

Labels

Custom Text

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))

Removing Labels

plot(ega.wmt, label = FALSE)

Size and Color

plot(ega.wmt, label.size = 6, label.color = "white")

Edges

Size

plot(ega.wmt, edge.size = 12)

Transparency

plot(ega.wmt, edge.alpha = 0.1)

Colors and Line Type

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
  )
)

Positive Only

plot(
  ega.wmt,
  edge.color = c(
    "darkgreen", # positive edges (default color)
    "white" # negative edges second
  )
)

Negative Only

plot(
  ega.wmt, edge.alpha = 1, # used to increase visibility
  edge.color = c(
    "white", # positive edges first
    "red" # negative edges (default color)
  )
)

Putting It Together

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"))

Saving Your Plot

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)

Advanced

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.

Reusing a Layout

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))

Sizing and Shaping Nodes by Community

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))

Accessibility: Colorblind-Safe Plots

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 Size

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)

Comparing Structures

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")
)

Beyond EGA: Other Plot Methods

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.