---
title: "Customizing the Hierarchy Diagram"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Customizing the Hierarchy Diagram}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

<!-- Generated by vignettes/precompute.R; DO NOT EDIT BY HAND.
     Edit ackwards-visualization.Rmd.orig and re-run `Rscript vignettes/precompute.R`.
     Freshness is enforced by tools/check-vignette-freshness.R. -->
<!-- precompute-stamp: source=ackwards-visualization.Rmd.orig md5=c695563288a5bcb1bc2a57be22fb7583 -->



`autoplot.ackwards()` exposes a large number of arguments for controlling the
appearance of the hierarchy diagram. The diagram shows the factors at every
level of the hierarchy, where a factor is an unobserved dimension that explains
why a set of items correlate. This vignette is a visual reference. Each section
demonstrates one group of arguments with rendered figures, so you can see the
effect before writing any code.

All options shown here are **presentation-only**. They do not change which
factors were extracted or how the between-level correlations were computed.
Options that change *which nodes appear* (`drop_pruned`, `compress_levels`) are
specific to the Forbes pruning extension and are covered in
`vignette("ackwards-forbes")`.

## Setup


``` r
library(ackwards)

bfi <- na.omit(bfi25)
x <- ackwards(bfi, k_max = 5, cor = "polychoric")
```

The default diagram for reference:


``` r
autoplot(x)
```

![plot of chunk base](assets/ackwards-visualization-base-1.png)

Factors are labeled `m{k}f{j}` (level k, factor j). Two edge aesthetics carry
the between-level correlations, and each one comes with its own legend. Arrow
**thickness** encodes the magnitude |r|, and edge **color** encodes the
direction (blue = positive, red-orange = negative). Level labels on the left
count factors per level.

Primary-parent edges are always positive after sign alignment. A red (negative)
edge is therefore a genuine *secondary* relationship, not an artifact.

## Encoding sign and magnitude

You choose which aesthetic carries which piece of information. `sign_by` picks
the channel for direction and `magnitude_by` picks the channel for |r|. No
aesthetic is ever mapped without a matching legend.

### `sign_by`: how direction is shown

`sign_by = "color"` (the default) uses `color_pos`/`color_neg`. `"linetype"`
draws positive edges solid and negative edges dashed, which frees color for a
single-hue figure. `"both"` uses color *and* linetype together. Negatives get a
distinct double-dash so they still read in greyscale, and the two are merged
into a single "Direction" legend. `"none"` drops sign encoding entirely.


``` r
autoplot(x, sign_by = "linetype")
```

![plot of chunk sign-by](assets/ackwards-visualization-sign-by-1.png)

``` r
autoplot(x, sign_by = "both")
```

![plot of chunk sign-by](assets/ackwards-visualization-sign-by-2.png)

### `magnitude_by`: how |r| is shown

By default `magnitude_by = "linewidth"` maps |r| to arrow thickness with a `|r|`
legend. Set `magnitude_by = "none"` for uniform-width edges (see also
`edge_linewidth`, below, to pin a specific width).


``` r
autoplot(x, magnitude_by = "none")
```

![plot of chunk magnitude-by](assets/ackwards-visualization-magnitude-by-1.png)

## Filtering edges

### `cut_show`: minimum |r| to display

Edges below `cut_show` are hidden entirely. Raising it produces a sparser
diagram that emphasises only the strongest connections.


``` r
# Default cut_show = 0.3 (already shown above)
autoplot(x, cut_show = 0.5)
```

![plot of chunk cut-show](assets/ackwards-visualization-cut-show-1.png)

## Edge colors

### `color_pos` / `color_neg`: custom direction colors

The default blue/red palette can be replaced with any colors recognised by R.
British spellings (`colour_pos`, `colour_neg`) are accepted as aliases.


``` r
autoplot(x, color_pos = "darkorchid", color_neg = "darkorange")
```

![plot of chunk colours](assets/ackwards-visualization-colours-1.png)

When sign is *not* encoded by color (`sign_by = "linetype"` or `"none"`), all
edges take the single `color_edge` (default black). That is the basis for the
Forbes (2023) publication style (see the worked example at the end of this
vignette).

## Monochrome mode

### `mono = TRUE`: a black-and-white convenience wrapper

`mono = TRUE` is shorthand for `sign_by = "linetype"` with black edges. Solid
lines are positive correlations, dashed lines are negative. `magnitude_by` still
applies, so `linewidth` continues to encode |r|.


``` r
autoplot(x, mono = TRUE)
```

![plot of chunk mono](assets/ackwards-visualization-mono-1.png)

`mono` suits black-and-white figures where the reader must distinguish positive
from negative edges. To label the edges with their exact values as well, add
`show_r = TRUE` (documented next).

## Correlation labels

### `show_r` / `r_digits`: annotate edges with r values

`show_r = TRUE` draws the rounded (signed) correlation at each edge midpoint.
`r_digits` controls the number of decimal places (default 2).


``` r
autoplot(x, show_r = TRUE)
```

![plot of chunk show-r](assets/ackwards-visualization-show-r-1.png)


``` r
autoplot(x, show_r = TRUE, r_digits = 1)
```

![plot of chunk show-r-digits](assets/ackwards-visualization-show-r-digits-1.png)

## Node labels

### `node_labels`: rename individual factors

`node_labels` is a named character vector mapping factor IDs to display strings.
Unspecified factors keep any name attached with `set_factor_labels()`, and fall
back to their `m{k}f{j}` labels. If you have already labelled the object with
`set_factor_labels()` (see `vignette("ackwards-interpret")`), those names appear
on the diagram automatically. Then `node_labels` is only needed to override a
particular node for this one plot.


``` r
autoplot(x, node_labels = c(
  m5f1 = "Neuro.",
  m5f2 = "Extra.",
  m5f3 = "Consc.",
  m5f4 = "Agree.",
  m5f5 = "Open."
))
```

![plot of chunk node-labels](assets/ackwards-visualization-node-labels-1.png)

Multi-line labels are supported via `\n`:


``` r
autoplot(x, node_labels = c(
  m5f1 = "Neuro-\nticism",
  m5f2 = "Extra-\nversion"
))
```

![plot of chunk node-labels-multiline](assets/ackwards-visualization-node-labels-multiline-1.png)

### `node_width` / `node_height`: size individual boxes to fit a label

A long substantive name may not fit the default box. `node_width` and
`node_height` each accept a **named vector** keyed by factor ID, which sizes
those boxes individually. Boxes you do not name keep the default. Increase
`min_sep` to make room when a box grows wider than the spacing between siblings.


``` r
autoplot(x,
  node_labels = c(m5f1 = "Neuroticism", m5f3 = "Conscientiousness"),
  node_width = c(m5f1 = 1.8, m5f3 = 2.2),
  min_sep = 2.4
)
```

![plot of chunk node-size](assets/ackwards-visualization-node-size-1.png)

### `label_template()`: generate the scaffold

Typing out every factor ID is tedious for large objects. `label_template()`
generates the full named vector in canonical diagram order and prints a
copy-pasteable `c(...)` literal you can edit and pass back to `node_labels`. It
also offers the Forbes (2023) letter convention (`"A1"`, `"B1"`, `"B2"`, …) as a
built-in style:


``` r
autoplot(x, node_labels = label_template(x, style = "forbes"))
```

![plot of chunk label-template-forbes](assets/ackwards-visualization-label-template-forbes-1.png)

For the full naming workflow (reading factors, the sign convention, and
choosing labels across the hierarchy) see `vignette("ackwards-interpret")`.

## Item content

### `show_items`: list the items under the deepest-level factors

For a publication figure it helps to show *what* each most-granular factor is
made of. Set `show_items = TRUE` to list the salient items beneath each
deepest-level (`k_max`) box. It shows the top `n_items` by `|loading|` at or
above `item_cut`, using the same extraction as `top_items()`. Variable labels
appear when the data carried
them into the fit. Here `na.omit()` stripped `bfi25`'s label attributes, so the
item IDs are shown. See `vignette("ackwards-interpret")` on keeping labels.


``` r
autoplot(x, show_items = TRUE, n_items = 4)
```

![plot of chunk show-items](assets/ackwards-visualization-show-items-1.png)

## Structural simplifications

### `primary_only = TRUE`: show only primary-parent edges

Setting `primary_only = TRUE` keeps only the single strongest edge per factor
(its primary parent), which produces a clean tree. Because skip-level edges are
never primary, this also suppresses curved arcs when `pairs = "all"` was used.


``` r
autoplot(x, primary_only = TRUE)
```

![plot of chunk primary-only](assets/ackwards-visualization-primary-only-1.png)

### `order`: arrange the deepest level by hand

The layout orders factors automatically to minimise edge crossings. But you may
want a specific left-to-right arrangement, to match a paper or to untangle a
figure. `order` fixes the order of the **deepest** (`k_max`) level. Supply that
level's factor IDs in the desired order. Every factor above stays centred over
its primary children, so fixing the leaf order rearranges the whole tree
coherently. Any arrangement of the hierarchy is reachable this way.


``` r
# Reverse the deepest level's left-to-right order
deepest <- paste0("m5f", 5:1)
autoplot(x, order = deepest)
```

![plot of chunk manual-order](assets/ackwards-visualization-manual-order-1.png)

### `drop_pruned` + `show_secondary`: the pruned view and its hidden correlations

Pruning (see `vignette("ackwards-forbes")`) flags redundant factors, factors
that persist across levels without changing. `drop_pruned = TRUE` then renders
the reduced hierarchy, joining each retained factor to its single strongest
surviving ancestor. That primary view hides every *other* between-level
correlation. `show_secondary = TRUE` adds them back: each kept cross-level pair
with `|r| >= cut_show` that is not a primary edge. They are drawn dimmed and
thinner beneath the primary arrows, so the sign colors stay intact. These are a
factor's weaker second parents *and* direct skip-level correlations the primary
path would otherwise obscure. A skip-level |r| is its own fact: correlation is
not transitive, so it need not equal the product along the path.


``` r
xp <- prune(x, "redundant")
#> ℹ Redundancy pruning (direct criterion, |r| ≥ 0.9) flagged 6 nodes.
#> ℹ Nodes are retained in the object; inspect with `x$prune$nodes` and
#>   `x$prune$chains`.
autoplot(xp, drop_pruned = TRUE) # primary view: one ancestor per factor
```

![plot of chunk drop-pruned-secondary](assets/ackwards-visualization-drop-pruned-secondary-1.png)

``` r
autoplot(xp, drop_pruned = TRUE, show_secondary = TRUE) # + secondary correlations
```

![plot of chunk drop-pruned-secondary](assets/ackwards-visualization-drop-pruned-secondary-2.png)

## Level labels

### `show_level_labels` / `level_label_size`

Level labels ("1 factor", "2 factors", …) are shown by default on the left
margin. They can be hidden or resized.


``` r
autoplot(x, show_level_labels = FALSE)
```

![plot of chunk level-labels-off](assets/ackwards-visualization-level-labels-off-1.png)


``` r
autoplot(x, show_level_labels = TRUE, level_label_size = 4)
```

![plot of chunk level-labels-size](assets/ackwards-visualization-level-labels-size-1.png)

## Arrowheads

### `show_arrows = FALSE`: plain line ends

By default, edges end with closed arrowheads. Setting `show_arrows = FALSE`
draws plain line ends. This applies to both straight edges and curved skip-level
arcs.


``` r
autoplot(x, show_arrows = FALSE)
```

![plot of chunk no-arrows](assets/ackwards-visualization-no-arrows-1.png)

## Edge width

### `edge_linewidth`: uniform vs. |r|-scaled width

By default, edge width is proportional to |r| (via `magnitude_by`). A numeric
`edge_linewidth` draws every edge at that constant width and removes the `|r|`
legend. It works like `magnitude_by = "none"`, but at a width you choose.


``` r
autoplot(x, edge_linewidth = 0.7)
```

![plot of chunk edge-linewidth](assets/ackwards-visualization-edge-linewidth-1.png)

## Layout orientation

### `direction = "horizontal"`: left-to-right layout

By default levels stack top-to-bottom (level 1 at top). `direction =
"horizontal"` lays them out left-to-right (level 1 at left), which fits wide
slides and posters. The level labels move to the bottom margin.


``` r
autoplot(x, direction = "horizontal")
```

![plot of chunk direction](assets/ackwards-visualization-direction-1.png)

## Legend

### `legend = FALSE`: suppress all guides

`legend = FALSE` removes all legends from the plot. It is most useful when the
diagram is self-explanatory or the legend duplicates information conveyed by
labels.


``` r
autoplot(x, legend = FALSE)
```

![plot of chunk no-legend](assets/ackwards-visualization-no-legend-1.png)

## Worked example: publication-ready figure

The following call reproduces the visual style of Forbes (2023): black lines of
uniform weight, plain line ends, correlation labels, and no legend. Setting both
direction colors to black yields a single-hue figure, and `legend = FALSE`
suppresses the now-redundant key.


``` r
autoplot(x,
  color_pos      = "black",
  color_neg      = "black",
  edge_linewidth = 0.6,
  show_arrows    = FALSE,
  show_r         = TRUE,
  legend         = FALSE
)
```

![plot of chunk pub-figure](assets/ackwards-visualization-pub-figure-1.png)

Combining with `node_labels` names the factors for the final figure:


``` r
autoplot(x,
  color_pos = "black",
  color_neg = "black",
  edge_linewidth = 0.6,
  show_arrows = FALSE,
  show_r = TRUE,
  legend = FALSE,
  node_labels = c(
    m5f1 = "Neuro.",
    m5f2 = "Extra.",
    m5f3 = "Consc.",
    m5f4 = "Agree.",
    m5f5 = "Open."
  )
)
```

![plot of chunk pub-figure-labeled](assets/ackwards-visualization-pub-figure-labeled-1.png)

For the pruned-factor variant of this figure (nodes omitted, spanning arrows)
see `vignette("ackwards-forbes")`.

## Saving plots

`autoplot()` returns an ordinary `ggplot` object, so save it with
`ggplot2::ggsave()`:


``` r
p <- autoplot(x, direction = "horizontal")
ggplot2::ggsave("hierarchy.png", p, width = 9, height = 5, dpi = 300)
```

`ackwards` does not re-export `ggsave()`, because that would move `ggplot2`
from Suggests into Imports. Call it from `ggplot2` directly.

---

## Diagnostic scree / criteria plot: `autoplot.suggest_k()`

`suggest_k()` also has its own `autoplot()` method. It produces a multi-panel
diagnostic with a scree plot, parallel analysis (which keeps a factor only when
it explains more variance than random data would), and VSS. It is documented
in depth in `vignette("ackwards-suggest-k")`. Here we only show that the same
`autoplot()` generic covers it.


``` r
sk <- suggest_k(bfi, seed = 42)
#> ℹ Running parallel analysis (20 iterations, PC + FA)...
#> ✔ Running parallel analysis (20 iterations, PC + FA)... [100ms]
#> 
#> ℹ Running MAP and VSS...
#> ✔ Running MAP and VSS... [33ms]
#> 
#> ℹ Running Comparison Data (CD)...
#> ✔ Running Comparison Data (CD)... [3.9s]
#> 
autoplot(sk)
```

![plot of chunk suggest_k_plot](assets/ackwards-visualization-suggest_k_plot-1.png)

## References

Forbes, M. K. (2023). Improving hierarchical models of individual differences:
An extension of Goldberg's bass-ackward method. *Psychological Methods*.
https://doi.org/10.1037/met0000546
