Skip to contents

shift_node_marks() prepares node markers for inferred regime shifts on an already plotted tree. For bifrost_search inputs, transitions are computed from the final multi-regime BMM mapped tree. Generic workflows can supply a shift_transitions() table, or a SIMMAP-style tree plus a named state_values vector.

Usage

shift_node_marks(
  x = NULL,
  tree = NULL,
  state_values = NULL,
  transitions = NULL,
  ic_weights = NULL,
  support_threshold = 0.9,
  letter_order = c("child_state", "node", "age"),
  marker_base_cex = 1.5,
  marker_scale_factor = 0.08,
  marker_transform = c("square_root", "log1p"),
  marker_max_cex = Inf
)

# S3 method for class 'shift_node_marks'
plot(
  x,
  rate_changes = c("increase", "decrease"),
  show_letters = TRUE,
  show_low_support = TRUE,
  show_legend = TRUE,
  marker_alpha = 0.75,
  letter_cex = 0.75,
  increase_fill = "white",
  decrease_fill = "#2b6cb0",
  marker_col = "#111827",
  low_support_cex = 0.7,
  low_support_col = grDevices::adjustcolor("black", alpha.f = 0.58),
  legend_position = "topleft",
  legend_cex = 0.72,
  ...
)

# S3 method for class 'shift_node_marks'
as.data.frame(
  x,
  row.names = NULL,
  optional = FALSE,
  component = c("marks", "increase_key", "summary"),
  ...
)

Arguments

x

A bifrost_search object, a shift_transitions() table, a SIMMAP-style phylo tree when state_values is supplied, or an object previously returned by shift_node_marks().

tree

Optional SIMMAP-style phylo tree for generic input mode.

state_values

Named numeric vector mapping SIMMAP state labels to state-associated values for generic input mode.

transitions

Optional precomputed transition table. When supplied, x is used only as a possible source of ic_weights.

ic_weights

Optional data frame with node and ic_weight_withshift columns. When omitted for a compatible bifrost_search object, x$ic_weights is used when available.

support_threshold

Numeric threshold used to flag low-support shifts from ic_weight_withshift.

letter_order

Ordering used when assigning letters to increase nodes. The default, "child_state", matches the Berv et al. (2026) manuscript convention by ordering increases by the destination regime state, with node number as a stable tie-breaker. Use "node" for node-number order or "age" for chronological order.

marker_base_cex, marker_scale_factor

Controls for marker size. Size is marker_base_cex + transformed(abs(percentage_change)) * marker_scale_factor.

marker_transform

Transformation applied to absolute percent change before scaling marker size.

marker_max_cex

Maximum marker size.

rate_changes

Which directional shifts to draw.

show_letters

Logical; draw letter labels for increase nodes.

show_low_support

Logical; draw an additional dot on shifts with ic_weight_withshift < support_threshold.

show_legend

Logical; draw a compact legend for the node marks.

marker_alpha

Alpha used for filled node markers.

letter_cex

Character expansion for letter labels.

increase_fill, decrease_fill

Fill colors for increase and decrease markers.

marker_col

Outline color for filled node markers.

low_support_cex, low_support_col

Size and color for low-support dots.

legend_position

Position passed to graphics::legend().

legend_cex

Character expansion passed to graphics::legend().

...

Reserved for future extensions. Supplying unused arguments to plot() or as.data.frame() methods is an error.

row.names, optional

Included for compatibility with base::as.data.frame(); ignored.

component

Component to extract with base::as.data.frame(). Use "marks", "increase_key", or "summary".

Value

shift_node_marks() returns an object of class "shift_node_marks" with marks, increase_key, low_support_nodes, summary, and settings. Plotting returns the same object invisibly. The as.data.frame() method returns the requested component as a plain data.frame.

Details

The workflow is intentionally plot-agnostic: first draw the tree with plot.rateMap() or another tree plotting function, then call plot() on the returned shift_node_marks object to add the node layer.

Examples

toy_tree <- ape::read.tree(text = "(((a:1,b:1):1,c:2):1,d:3);")
toy_tree <- phytools::paintSubTree(
  tree = toy_tree,
  node = ape::Ntip(toy_tree) + 1L,
  state = "0"
)
toy_tree <- phytools::paintSubTree(
  tree = toy_tree,
  node = ape::Ntip(toy_tree) + 2L,
  state = "1"
)
toy_tree <- phytools::paintSubTree(
  tree = toy_tree,
  node = ape::Ntip(toy_tree) + 3L,
  state = "2"
)
marks <- shift_node_marks(
  tree = toy_tree,
  state_values = c("0" = 1, "1" = 4, "2" = 2)
)
marks$increase_key
#>   letter child_state node age percentage_change ic_weight_withshift
#> 1      A           1    6   2               300                  NA