--- title: "Prepare Forest Data with Helper Functions" output: rmarkdown::html_vignette: highlight: pygments vignette: > %\VignetteIndexEntry{Prepare Forest Data with Helper Functions} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 8, fig.height = 4.5 ) ``` ```{r setup} library(ggforestplotR) library(ggplot2) ``` This short article covers the `forest_data` interchange object and the helper functions that prepare it before a plot is drawn. ## Use `as_forest_data()` to standardize a coefficient table `as_forest_data()` converts your column names into the internal structure used by `ggforestplotR`. The result is a `forest_data` data-frame subclass containing the columns expected by `ggforestplot()`, `add_forest_table()`, and `add_split_table()`. ```{r as-forest-data} raw_coefs <- data.frame( variable = c("Age", "BMI", "Treatment"), beta = c(0.10, -0.08, 0.34), lower = c(0.02, -0.16, 0.12), upper = c(0.18, 0.00, 0.56), display = c("Age", "BMI", "Treatment"), section = c("Clinical", "Clinical", "Treatment"), sample_size = c(120, 115, 98), p_value = c(0.04, 0.15, 0.001) ) forest_ready <- as_forest_data( data = raw_coefs, term = "variable", estimate = "beta", conf.low = "lower", conf.high = "upper", label = "display", grouping = "section", n = "sample_size", p.value = "p_value", estimate_scale = "identity", effect_label = "Beta", conf.level = 0.95 ) ``` Use `forest_metadata()` to inspect the semantic and provenance contract. The complete fitted model and a duplicate copy of the source data are not retained. ```{r inspect-forest-metadata} forest_metadata(forest_ready)[c( "estimate_scale", "axis_transform", "effect_label", "conf_level", "reference_value", "source_model", "source_package", "source_columns" )] ``` Once the data are standardized, you can pass them straight into `ggforestplot()`. ```{r helper-to-plot} ggforestplot(forest_ready) ``` ## Use `as_forest_data()` for model objects If `broom` is available, the model-specific `as_forest_data()` method pulls coefficient estimates and confidence limits from a fitted model. The method also assigns model semantics such as `Beta`, `OR`, or `HR`. ```{r tidy-model} fit <- lm(mpg ~ wt + hp + qsec, data = mtcars) model_ready <- as_forest_data(fit) ``` The returned object can be passed directly into `ggforestplot()`. ```{r helper-to-plot-model} ggforestplot(model_ready) ``` `tidy_forest_model()` remains available as a compatibility wrapper and returns the same `forest_data` object. ## Derive subgroup effects from an interaction model For a fitted model with one continuous-by-factor interaction, `subgroup = "auto"` identifies the factor from model metadata and estimates the focal variable's average slope within each observed factor level. ```{r model-subgroup-effects} interaction_fit <- lm(wt ~ hp + mpg * factor(cyl) + qsec, data = mtcars) subgroup_ready <- tidy_forest_model( interaction_fit, subgroup = "auto", focal = "mpg" ) as.data.frame(subgroup_ready)[, c( "term", "subgroup", "estimate", "conf.low", "conf.high", "p.value" )] ggforestplot(subgroup_ready, striped_rows = TRUE) + add_forest_table(columns = c("term", "estimate", "p")) ``` These rows are average `mpg` slopes within the `cyl` levels. They come from `marginaleffects::avg_slopes()` using the original model and its covariance matrix. No subgroup-specific models are refitted. The focal main effect, factor main-effect coefficients, and raw interaction coefficients are replaced by the derived block; unrelated coefficients remain standalone in formula order. The same interaction can be selected explicitly: ```{r explicit-model-subgroup-effects} tidy_forest_model( interaction_fit, subgroup = "cyl", focal = "mpg" ) ``` Automatic selection currently requires exactly one unambiguous continuous-by-factor interaction. Factor-by-factor effects require explicit predictor names and use `marginaleffects::avg_comparisons()`. Continuous subgroups, transformed focal terms, multiple selected interactions, and three-way interactions are rejected. Logistic and Cox effects are calculated on their model scale and shown as odds ratios and hazard ratios by default, not as marginal probabilities or survival probabilities. The canonical `p.value` combines two compatible display roles: ordinary covariates retain their coefficient p-values, while every derived subgroup block receives an omnibus Wald test of the selected interaction. The display layer moves that interaction p-value to the subgroup header and leaves the individual levels blank, so one table column can present both kinds of tests. Use `p_method = "level"` when the subgroup-specific slope or comparison tests are the quantities that should appear beside the individual estimates: ```{r model-subgroup-level-p-values} level_p_ready <- tidy_forest_model( interaction_fit, subgroup = "auto", focal = "mpg", p_method = "level" ) ggforestplot(level_p_ready, striped_rows = TRUE) + add_forest_table(columns = c("term", "estimate", "p")) ``` Both methods use the same canonical `p.value` column. `"overall"` changes the test and places it on the subgroup header; `"level"` retains the tests returned for the derived effects and places them on the subgroup rows.