Plotting Causal Graphs

library(caugi)

The caugi package provides a flexible plotting system built on grid graphics for visualizing causal graphs. This vignette demonstrates how to create plots with different layout algorithms and customize their appearance.

Basic Plotting

The simplest way to visualize a caugi graph is with the plot() function:

# Create a simple DAG
cg <- caugi(
  A %-->% B + C,
  B %-->% D,
  C %-->% D,
  class = "DAG"
)

# Plot with default settings
plot(cg)

By default, plot() automatically selects the best layout algorithm based on your graph type. For graphs with only directed edges, it uses the Sugiyama hierarchical layout. For graphs with other edge types, it uses the Fruchterman-Reingold force-directed layout.

Layout Algorithms

The caugi package provides three layout algorithms, each optimized for different use cases.

Sugiyama (Hierarchical Layout)

The Sugiyama layout (Sugiyama, Tagawa, and Toda 1981) is ideal for directed acyclic graphs (DAGs). It arranges nodes in layers to emphasize hierarchical structure and causal flow from top to bottom, minimizing edge crossings.

# Create a more complex DAG
dag <- caugi(
  X1 %-->% M1 + M2,
  X2 %-->% M2 + M3,
  M1 %-->% Y,
  M2 %-->% Y,
  M3 %-->% Y,
  class = "DAG"
)

# Use Sugiyama layout explicitly
plot(dag, layout = "sugiyama")

Best for: DAGs, causal models, hierarchical structures

Limitations: Only works with directed edges

Fruchterman-Reingold (Spring-Electrical)

The Fruchterman-Reingold layout (Fruchterman and Reingold 1991) uses a physical simulation where edges act as springs and nodes repel each other like charged particles. It produces organic, symmetric layouts with relatively uniform edge lengths.

# Create a graph with bidirected edges (ADMG)
admg <- caugi(
  A %-->% C,
  B %-->% C,
  A %<->% B, # Bidirected edge (latent confounder)
  class = "ADMG"
)

# Fruchterman-Reingold handles all edge types
plot(admg, layout = "fruchterman-reingold")

Best for: General-purpose visualization, graphs with mixed edge types

Advantages: Fast, works with all edge types, produces balanced layouts

Kamada-Kawai (Stress Minimization)

The Kamada-Kawai layout (Kamada and Kawai 1989) minimizes “stress” by making Euclidean distances in the plot proportional to graph-theoretic distances. This produces high-quality layouts that better preserve the global structure compared to Fruchterman-Reingold.

# Create an undirected graph
ug <- caugi(
  A %---% B,
  B %---% C + D,
  C %---% D,
  class = "UG"
)

# Kamada-Kawai for publication-quality visualization
plot(ug, layout = "kamada-kawai")

Best for: Publication-quality figures, when accurate distance representation matters

Advantages: Better global structure preservation, deterministic results

Comparing Layouts

You can compute and examine layout coordinates directly using caugi_layout():

# Compute layouts
layout_sug <- caugi_layout(dag, method = "sugiyama")
layout_fr <- caugi_layout(dag, method = "fruchterman-reingold")
layout_kk <- caugi_layout(dag, method = "kamada-kawai")

# Examine coordinates
head(layout_sug)
#>   name  x  y
#> 1   X1  5  0
#> 2   X2 15  0
#> 3   M1  0 10
#> 4   M2 10 10
#> 5   M3 20 10
#> 6    Y 10 20

Customizing Plots

The plot() function provides extensive customization options for nodes, edges, and labels.

Node Styling

Customize node appearance with the node_style parameter:

# Customize node colors and sizes
plot(
  cg,
  node_style = list(
    fill = "lightblue", # Fill color
    col = "darkblue", # Border color
    lwd = 2, # Border width
    padding = 4, # Text padding (mm)
    size = 1.2 # Size multiplier
  )
)

Available node style parameters:

Edge Styling

Customize edge appearance with the edge_style parameter. You can set global options or customize each edge type separately:

# Global edge styling
plot(
  dag,
  edge_style = list(
    col = "darkgray", # Edge color
    lwd = 1.5, # Edge width
    arrow_size = 4 # Arrow size (mm)
  )
)

# Per-type edge styling for ADMG
plot(
  admg,
  layout = "fruchterman-reingold",
  edge_style = list(
    directed = list(col = "blue", lwd = 2),
    bidirected = list(col = "red", lwd = 2, lty = "dashed")
  )
)

Available edge style parameters:

Label Styling

Customize node labels with the label_style parameter:

# Customize label appearance
plot(
  cg,
  label_style = list(
    col = "white", # Text color
    fontsize = 12, # Font size
    fontface = "bold", # Font face
    fontfamily = "sans" # Font family
  ),
  node_style = list(
    fill = "navy" # Dark background for white text
  )
)

Available label style parameters (passed to gpar()):

Combining Customizations

Put it all together for a fully customized plot:

# Create a publication-ready plot
plot(
  dag,
  layout = "sugiyama",
  node_style = list(
    fill = "#E8F4F8",
    col = "#2C5F77",
    lwd = 1.5,
    padding = 3,
    size = 1.1
  ),
  edge_style = list(
    col = "#555555",
    lwd = 1.2,
    arrow_size = 3.5
  ),
  label_style = list(
    col = "#1A1A1A",
    fontsize = 11,
    fontface = "bold"
  )
)

Working with Different Graph Types

The plotting system works seamlessly with all supported graph types.

Partially Directed Acyclic Graphs (PDAGs)

# Create a PDAG with both directed and undirected edges
pdag <- caugi(
  A %-->% B,
  B %---% C, # Undirected edge
  C %-->% D,
  class = "PDAG"
)

plot(
  pdag,
  edge_style = list(
    directed = list(col = "blue"),
    undirected = list(col = "gray", lwd = 2)
  )
)

Acyclic Directed Mixed Graphs (ADMGs)

# Create an ADMG with confounding
complex_admg <- caugi(
  X %-->% M1 + M2,
  M1 %-->% Y,
  M2 %-->% Y,
  M1 %<->% M2, # Latent confounder between mediators
  class = "ADMG"
)

plot(
  complex_admg,
  layout = "kamada-kawai",
  node_style = list(fill = "lavender"),
  edge_style = list(
    directed = list(col = "black", lwd = 1.5),
    bidirected = list(col = "red", lwd = 1.5, lty = "dashed", arrow_size = 3)
  )
)

Undirected Graphs (UGs)

# Create a Markov network
markov <- caugi(
  A %---% B + C,
  B %---% D,
  C %---% D + E,
  D %---% E,
  class = "UG"
)

plot(
  markov,
  layout = "fruchterman-reingold",
  node_style = list(
    fill = "lightyellow",
    col = "orange",
    lwd = 2
  ),
  edge_style = list(col = "orange")
)

Advanced Usage

Manual Layouts

You can compute layouts separately and reuse them:

# Compute layout once
coords <- caugi_layout(dag, method = "sugiyama")

# The layout can be used for analysis or custom plotting
print(coords)
#>   name  x  y
#> 1   X1  5  0
#> 2   X2 15  0
#> 3   M1  0 10
#> 4   M2 10 10
#> 5   M3 20 10
#> 6    Y 10 20

# Plot uses the computed layout internally
plot(dag, layout = "sugiyama")

Integration with Grid Graphics

caugi plots are built on grid graphics, and provide access to the underlying grid grob object in the @grob slot of the plot output. This allows for further customization using grid functions.

# Create a plot
p <- plot(cg)

# The plot is a grid graphics object
class(p)
#> [1] "caugi::caugi_plot" "S7_object"

# You can manipulate it with grid functions
library(grid)

# Draw the plot rotated by 30 degrees
pushViewport(viewport(angle = 30))
grid.draw(p@grob)
popViewport()

This also allows you to arrange multiple caugi plots using packages like gridExtra.

References

Fruchterman, Thomas M. J., and Edward M. Reingold. 1991. “Graph Drawing by Force-Directed Placement.” Software: Practice and Experience 21 (11): 1129–64. https://doi.org/10.1002/spe.4380211102.
Kamada, Tomihisa, and Satoru Kawai. 1989. “An Algorithm for Drawing General Undirected Graphs.” Information Processing Letters 31 (1): 7–15. https://doi.org/10.1016/0020-0190(89)90102-6.
Sugiyama, Kozo, Shojiro Tagawa, and Mitsuhiko Toda. 1981. “Methods for Visual Understanding of Hierarchical System Structures.” IEEE Transactions on Systems, Man, and Cybernetics 11 (2): 109–25. https://doi.org/10.1109/TSMC.1981.4308636.