Skip to contents

POWERUP uses SHAP values to explain how molecular features contribute to its predictions. For an individual sample, the fitted model prediction can be represented as a baseline value plus the additive contributions of its features. Positive and negative SHAP values move the modeled outcome higher or lower, respectively.

POWERUP provides several complementary views of these explanations. A waterfall plot shows how features contribute to one prediction, a SHAP scatter places those effects in the context of the training cohort, and aggregate contribution plots identify features that contribute most strongly across many samples. Explanation path analysis then asks a different question: whether samples with the same predicted response reach that prediction through recurrent, distinct SHAP patterns.

SHAP values explain the fitted model, not biological causality. A large SHAP contribution means that a feature strongly influenced the model prediction; it does not show that experimentally changing the feature would change the response.

Recreate the example predictions

The first examples use the same compact DepMap Public 26Q1 demonstration data introduced in Get Started with POWERUP. We fit a CTNNB1 dependency model and generate predictions for two held-out samples. See Example data and provenance for details about the bundled data.

expression_path <- system.file("extdata", "powerup_example_gene_expression.csv", package = "powerup")
dependency_path <- system.file("extdata", "powerup_example_gene_dependency.csv", package = "powerup")
user_path <- system.file("extdata", "powerup_example_user_matrix.csv", package = "powerup")

example_expression <- read.csv(expression_path, check.names = FALSE)
example_dependency <- read.csv(dependency_path, check.names = FALSE)
example_user <- read.csv(user_path, check.names = FALSE)

prepared <- prepare_powerup_data(gene_expression = example_expression, response = example_dependency, targets = "CTNNB1", user_matrix = example_user)
models <- fit_powerup_models(prepared, seed = 123L)
models <- add_powerup_predictions(models, prepared)

The two user samples now have both predictions and SHAP contributions that can be examined with the functions below.

Explain an individual prediction

plot_contributions_to_sample() provides the most direct explanation of an individual POWERUP prediction. The waterfall begins from the model’s SHAP baseline and shows how the largest feature contributions move the prediction toward its final value. This baseline is the constant reference value returned by the fitted XGBoost model before the sample-specific feature contributions are added.

Here we explain the CTNNB1 dependency predictions for the two held-out samples:

plot_contributions_to_sample(
  models,
  prepared,
  perturbations = "CTNNB1",
  samples = c("ACH-000957", "ACH-002024"),
  source = "user",
  n_features = 6,
  n_columns = 1,
  fixed_axis = TRUE
)

Waterfall plots showing feature contributions to CTNNB1 dependency predictions for two held-out samples.

The feature labels also report the corresponding feature values for the selected sample. n_features controls how many of the largest absolute SHAP contributions are displayed individually; any remaining contributions are combined into an “other terms” contribution so that the displayed contributions still connect the baseline to the final prediction.

Training samples can be examined in the same way using the default source = "training". For user samples predicted with add_powerup_predictions(), use source = "user" as above.

Notice that the same feature, AXIN2 for example, can have a positive contribution in one sample and a negative contribution in another depending on its expression value. The SHAP contribution shown near each arrow reflects that feature’s influence on the model prediction for that specific sample.

Place a prediction in context

A waterfall shows which features matter for one sample, while plot_shap_scatter() puts those feature values and SHAP contributions in the context of all training data.

Each panel plots the feature value on the x-axis against its exact SHAP contribution on the y-axis. Selected training or user samples can then be highlighted on top of this reference distribution to see how they compare.

Here we show the top six CTNNB1 features and overlay the same two user samples:

plot_shap_scatter(
  models,
  prepared,
  perturbation = "CTNNB1",
  n_features = 6,
  samples = c("ACH-000957", "ACH-002024"),
  source = "user",
  sample_colors = c("red", "blue")
)

SHAP scatter plots placing CTNNB1 feature effects for two held-out samples in the training-cohort context.

These plots help show how the predictive model evaluates the contribution of a feature across its observed values. The highlighted samples show where an individual prediction falls within that learned relationship.

Because tree-based models can capture nonlinear relationships, the association between a feature value and its SHAP contribution need not be linear or monotonic. Different features can also show different thresholds, plateaus, or changes in direction.

The color of the training points reflects the observed outcome. Samples can therefore have a strong dependency on the target even when one of the displayed features has a low or negative SHAP contribution, and vice versa. This illustrates how the final model prediction reflects the combined contributions of many features rather than any single feature alone.

By default, this function selects the most influential features based on the training cohort. Specific features can instead be supplied with the features argument.

Summarize explanations across samples

Local explanations can also be aggregated to identify features that contribute most strongly across a cohort. plot_top_contributors() ranks each feature by the sum of its absolute SHAP contributions across the selected samples.

For the CTNNB1 training cohort:

plot_top_contributors(
  models,
  models_to_use = "CTNNB1",
  data_to_use = "training",
  n_predictors = 10,
  n_columns = 1
)

Aggregate SHAP contribution plot showing the strongest CTNNB1 features across the training cohort.

Because the aggregation uses absolute SHAP values, this view measures the overall magnitude of a feature’s influence rather than whether the feature consistently increases or decreases predictions. A feature can therefore rank highly even when its direction differs across samples.

The same summary can be generated for the prediction cohort after add_powerup_predictions():

plot_top_contributors(
  models,
  models_to_use = "CTNNB1",
  data_to_use = "new_data",
  n_predictors = 10,
  n_columns = 1
)

For analyses containing many fitted models, summarize_contributions() provides the corresponding aggregate SHAP values as a model-by-feature matrix for downstream analysis.

Together, these plots help investigate what drove one prediction, how those effects compare with the reference cohort, and which features matter most across a collection of samples. However, a single aggregate summary can still hide distinct combinations of feature effects that recur among samples with the same predicted response.


Explanation path diversity

POWERUP explanation paths are designed to capture this additional structure. Two samples can receive the same predicted response class (Sensitive or Resistant) while reaching that prediction through different combinations of SHAP contributions (we call these “explanation paths”). Explanation path analysis identifies recurrent SHAP profiles within the predicted Sensitive and Resistant classes and summarizes how many distinct explanation patterns are supported by the training data.

Path discovery is intentionally based on training samples only. Prediction samples do not define or change the paths. When the original fitted models contain new predictions, assign_explanation_paths() can subsequently project those samples onto the fixed training-defined paths.

For this section, we use the bundled explanation-model object and focus on its CTNNB1 model, which was fitted on a larger reference cohort. This provides more realistic path structure than the compact 300-sample example while keeping file sizes manageable. See Example data and provenance for details about the bundled data.

Load the explanation models

The bundled RDS file retains the training SHAP values and model metadata needed for path analysis. It omits the original expression and response matrices, fitted boosters, uncertainty models, and prediction data.

model_path <- system.file("extdata", "powerup_example_explanation_models.rds", package = "powerup", mustWork = TRUE)
path_models <- readRDS(model_path)

ctnnb1_target <- "ko_ctnnb1"

The bundled object uses POWERUP’s processed perturbation identifier ko_ctnnb1, while the text of this guide refers to the corresponding gene symbol CTNNB1 for readability.

Calculate explanation paths

Calculate paths for the CTNNB1 example target:

paths <- calculate_explanation_paths(path_models, targets = ctnnb1_target)
#> Calculating explanation paths: 1 of 1
paths
#> $results
#> $results$ko_ctnnb1
#> $results$ko_ctnnb1$target
#> [1] "ko_ctnnb1"
#> 
#> $results$ko_ctnnb1$class_summary
#> # A tibble: 2 × 52
#>   target    mean_r prediction_class n_cell_lines eligible_for_multipath
#>   <chr>      <dbl> <chr>                   <int> <lgl>                 
#> 1 ko_ctnnb1  0.740 Sensitive                 119 TRUE                  
#> 2 ko_ctnnb1  0.740 Resistant                1021 TRUE                  
#> # ℹ 47 more variables: n_supported_cell_lines <int>,
#> #   supported_cell_line_fraction <dbl>, n_outlier_cell_lines <int>,
#> #   outlier_fraction <dbl>, selected_raw_candidate_k <int>,
#> #   selected_path_count <int>, effective_path_count <dbl>,
#> #   dominant_path_fraction <dbl>, dominant_path_fraction_of_class <dbl>,
#> #   path_entropy <dbl>, path_evenness <dbl>, selected_silhouette <dbl>,
#> #   selected_stability_ari <dbl>, candidate_selection_score <dbl>, …
#> 
#> $results$ko_ctnnb1$path_summary
#> # A tibble: 4 × 11
#>   prediction_class explanation_path_id n_cell_lines fraction_of_supported
#>   <chr>                          <int>        <int>                 <dbl>
#> 1 Sensitive                          1           89                0.817 
#> 2 Sensitive                          2           20                0.183 
#> 3 Resistant                          1          879                0.934 
#> 4 Resistant                          2           62                0.0659
#> # ℹ 7 more variables: mean_prediction <dbl>, median_prediction <dbl>,
#> #   min_prediction <dbl>, max_prediction <dbl>, predominant_features <chr>,
#> #   distinguishing_features <chr>, fraction_of_class <dbl>
#> 
#> $results$ko_ctnnb1$assignments
#> # A tibble: 1,140 × 8
#>    target    matrix_row_index sample     prediction_class reconstructed_predic…¹
#>    <chr>                <int> <chr>      <chr>                             <dbl>
#>  1 ko_ctnnb1                5 ACH-000461 Sensitive                         0.932
#>  2 ko_ctnnb1                7 ACH-002023 Sensitive                         0.943
#>  3 ko_ctnnb1               12 ACH-001098 Sensitive                         0.805
#>  4 ko_ctnnb1               15 ACH-000769 Sensitive                         0.892
#>  5 ko_ctnnb1               16 ACH-000421 Sensitive                         0.699
#>  6 ko_ctnnb1               33 ACH-000138 Sensitive                         0.547
#>  7 ko_ctnnb1               54 ACH-001991 Sensitive                         0.849
#>  8 ko_ctnnb1               73 ACH-000880 Sensitive                         0.900
#>  9 ko_ctnnb1               76 ACH-000552 Sensitive                         0.957
#> 10 ko_ctnnb1               78 ACH-000222 Sensitive                         0.768
#> # ℹ 1,130 more rows
#> # ℹ abbreviated name: ¹​reconstructed_prediction
#> # ℹ 3 more variables: explanation_path_id <int>, is_outlier <lgl>,
#> #   assignment_type <chr>
#> 
#> $results$ko_ctnnb1$diagnostics
#> # A tibble: 14 × 25
#>    prediction_class candidate_k actual_raw_cluster_count raw_cluster_sizes_des…¹
#>    <chr>                  <int>                    <int> <chr>                  
#>  1 Sensitive                  2                        2 116, 3                 
#>  2 Sensitive                  3                        3 116, 2, 1              
#>  3 Sensitive                  4                        4 109, 7, 2, 1           
#>  4 Sensitive                  5                        5 109, 6, 2, 1, 1        
#>  5 Sensitive                  6                        6 89, 20, 6, 2, 1, 1     
#>  6 Sensitive                  7                        7 89, 20, 6, 1, 1, 1, 1  
#>  7 Sensitive                  8                        8 89, 19, 6, 1, 1, 1, 1,…
#>  8 Resistant                  2                        2 975, 46                
#>  9 Resistant                  3                        3 913, 62, 46            
#> 10 Resistant                  4                        4 879, 62, 46, 34        
#> 11 Resistant                  5                        5 810, 69, 62, 46, 34    
#> 12 Resistant                  6                        6 810, 62, 52, 46, 34, 17
#> 13 Resistant                  7                        7 810, 62, 52, 46, 34, 1…
#> 14 Resistant                  8                        8 795, 62, 52, 46, 34, 1…
#> # ℹ abbreviated name: ¹​raw_cluster_sizes_descending
#> # ℹ 21 more variables: min_supported_path_size_required <dbl>,
#> #   n_supported_paths <int>, supported_path_sizes_descending <chr>,
#> #   n_supported_cell_lines <int>, supported_cell_line_fraction <dbl>,
#> #   n_outlier_clusters <int>, n_outlier_cell_lines <int>,
#> #   outlier_fraction <dbl>, silhouette <dbl>, stability_ari <dbl>,
#> #   effective_path_count <dbl>, dominant_path_fraction <dbl>, …
#> 
#> $results$ko_ctnnb1$feature_summary
#> # A tibble: 72 × 7
#>    prediction_class explanation_path_id n_cell_lines_in_path feature mean_shap
#>    <chr>                          <int>                <int> <chr>       <dbl>
#>  1 Sensitive                          1                   89 axin2     0.178  
#>  2 Sensitive                          1                   89 bmp4      0.0536 
#>  3 Sensitive                          1                   89 rnf43     0.0473 
#>  4 Sensitive                          1                   89 nkd1      0.0295 
#>  5 Sensitive                          1                   89 krba1     0.0204 
#>  6 Sensitive                          1                   89 bcl2l15   0.0183 
#>  7 Sensitive                          1                   89 eps8l3    0.0178 
#>  8 Sensitive                          1                   89 gpr35     0.0164 
#>  9 Sensitive                          1                   89 cdx2      0.0110 
#> 10 Sensitive                          1                   89 hnf4a     0.00990
#> # ℹ 62 more rows
#> # ℹ 2 more variables: mean_abs_shap <dbl>,
#> #   mean_shap_difference_vs_other_paths <dbl>
#> 
#> 
#> 
#> $errors
#> # A tibble: 0 × 0
#> 
#> $parameters
#> $parameters$min_mean_r
#> NULL
#> 
#> $parameters$response_cutoff
#> NULL
#> 
#> $parameters$max_paths
#> [1] 8
#> 
#> $parameters$min_path_size
#> [1] 10
#> 
#> $parameters$min_path_fraction
#> [1] 0.05
#> 
#> $parameters$max_outlier_fraction
#> [1] 0.1
#> 
#> $parameters$min_silhouette
#> [1] 0.3
#> 
#> $parameters$min_stability_ari
#> [1] 0.75
#> 
#> $parameters$stability_repeats
#> [1] 25
#> 
#> $parameters$stability_fraction
#> [1] 0.8
#> 
#> $parameters$driver_thresholds
#> [1] 0.5 0.7 0.9
#> 
#> $parameters$top_n_features
#> [1] 10
#> 
#> $parameters$distance
#> [1] "correlation"
#> 
#> $parameters$seed
#> [1] 101
#> 
#> $parameters$source
#> [1] "training_shap"
#> 
#> 
#> attr(,"class")
#> [1] "powerup_explanation_paths" "list"

Note: Using targets = NULL, the default, analyzes every eligible target in the supplied model object:

Within each predicted response class, POWERUP represents training samples in a common space of named SHAP features, calculates SHAP-profile distance, and performs average-linkage hierarchical clustering. Sensitive and Resistant samples are analyzed separately, so a sample predicted to be Sensitive will not cluster with Resistant training samples.

The default criteria are:

Criterion Default
Maximum candidate paths 8
Minimum path size 10 training samples
Minimum path fraction 5% of the prediction class
Maximum outlier fraction 10%
Minimum silhouette 0.30
Minimum stability ARI 0.75
Stability repeats 25
Stability subsample 80%

A supported path must contain at least the larger of the minimum path size and the minimum class fraction. Small branches are bundled together as outliers, but a multipath solution is retained only when the outlier, silhouette, and stability criteria are satisfied. If no acceptable multipath solution is found, the prediction class is represented by a single explanation path.

Please note that a class with fewer than twice the minimum path size cannot contain two paths that each satisfy that minimum. For example, a perturbation with only 15 sensitive cell lines cannot support two distinct explanation paths at 10 samples per path minimum. POWERUP retains these classes but marks them with eligible_for_multipath = FALSE.

Visualize explanation paths for one target

plot_explanation_paths() helps us visualize the supported paths for one target:

ctnnb1_plot <- plot_explanation_paths(paths, target = ctnnb1_target, models = path_models, top_n = 10)
ctnnb1_plot

CTNNB1 explanation-path figure showing path hierarchy, prediction distributions, and path-specific SHAP features.

The figure combines three views of the explanations: the Sensitive and Resistant path hierarchy, the distribution of model predictions within each supported path, and a signed mean-SHAP heatmap showing features that characterize those paths.

By default, the heatmap features are statistical path markers. For each path, POWERUP compares per-sample SHAP values with the other supported paths in the same response class using a Wilcoxon rank-sum test, followed by Benjamini-Hochberg correction. The default marker filters require an adjusted p-value of 0.05 or less, an absolute mean-SHAP difference of at least 0.005, and a nonzero SHAP value in at least half of one comparison group. Positive markers are shown by default. When a response class contains only one supported path, there is no path-vs-path comparison, so its displayed features fall back to those with the largest mean absolute SHAP values.

Notice that in the case of CTNNB1, the Sensitive class supports two distinct explanation paths: one with high AXIN2 SHAP, a classic biomarker of activated Wnt signaling, and one where BMP4 is more dominant.

The complete marker statistics are attached to the returned plot:

ctnnb1_markers <- attr(ctnnb1_plot, "path_markers")
head(ctnnb1_markers)
#> # A tibble: 6 × 16
#>   prediction_class explanation_path_id feature path_mean_shap
#>   <chr>                          <int> <chr>            <dbl>
#> 1 Sensitive                          1 a1bg        -0.0000171
#> 2 Sensitive                          1 aadat        0.000192 
#> 3 Sensitive                          1 abca2        0        
#> 4 Sensitive                          1 abhd17c      0        
#> 5 Sensitive                          1 abhd2        0.00165  
#> 6 Sensitive                          1 ache         0.000215 
#> # ℹ 12 more variables: other_paths_mean_shap <dbl>, mean_shap_difference <dbl>,
#> #   absolute_mean_shap_difference <dbl>, path_nonzero_fraction <dbl>,
#> #   other_paths_nonzero_fraction <dbl>, maximum_nonzero_fraction <dbl>,
#> #   wilcoxon_u <dbl>, common_language_probability <dbl>,
#> #   rank_biserial_correlation <dbl>, absolute_rank_biserial_correlation <dbl>,
#> #   p_value <dbl>, adjusted_p_value <dbl>

Note for large POWERUP jobs

In large runs, saving the fitted model objects can be resource-intensive. If the original fitted model object is unavailable, the plot can instead use the predominant features already stored during path calculation:

plot_explanation_paths(paths, target = ctnnb1_target, feature_selection = "mean_absolute_shap")

Summarize explanation diversity

summarize_explanation_paths() reduces the detailed path object to class-level and path-level tables:

path_summary <- summarize_explanation_paths(paths)

per_class contains one row for each target and predicted response class. A compact set of the most useful fields is:

path_summary$per_class[, c(
  "target",
  "prediction_class",
  "n_cell_lines",
  "eligible_for_multipath",
  "selected_path_count",
  "effective_path_count",
  "selected_silhouette",
  "selected_stability_ari",
  "dominant_path_fraction",
  "top_driver_feature",
  "n_features_50pct"
)]
#> # A tibble: 2 × 11
#>   target    prediction_class n_cell_lines eligible_for_multipath
#>   <chr>     <chr>                   <int> <lgl>                 
#> 1 ko_ctnnb1 Sensitive                 119 TRUE                  
#> 2 ko_ctnnb1 Resistant                1021 TRUE                  
#> # ℹ 7 more variables: selected_path_count <int>, effective_path_count <dbl>,
#> #   selected_silhouette <dbl>, selected_stability_ari <dbl>,
#> #   dominant_path_fraction <dbl>, top_driver_feature <chr>,
#> #   n_features_50pct <int>

selected_path_count is the number of supported paths retained for the class. effective_path_count additionally reflects how evenly samples are distributed among those paths, while dominant_path_fraction reports the fraction of supported samples belonging to the largest path. The silhouette and stability ARI summarize separation and reproducibility of a retained multipath solution.

top_driver_feature identifies the feature contributing most strongly to overall between-path SHAP separation. n_features_50pct reports the minimum number of features required to account for 50% of the between-path SHAP dispersion.

per_path contains one row for each supported path. It reports the path size and prediction distribution together with two complementary feature summaries:

  • predominant_features are features with the largest absolute mean SHAP values within that path.
  • distinguishing_features are features with the largest signed mean-SHAP differences between that path and the other supported paths in the same response class.

For CTNNB1:

path_summary$per_path[
  path_summary$per_path$target == ctnnb1_target,
  c(
    "target",
    "prediction_class",
    "explanation_path_id",
    "n_cell_lines",
    "fraction_of_class",
    "mean_prediction",
    "predominant_features",
    "distinguishing_features"
  )
]
#> # A tibble: 4 × 8
#>   target    prediction_class explanation_path_id n_cell_lines fraction_of_class
#>   <chr>     <chr>                          <int>        <int>             <dbl>
#> 1 ko_ctnnb1 Sensitive                          1           89            0.748 
#> 2 ko_ctnnb1 Sensitive                          2           20            0.168 
#> 3 ko_ctnnb1 Resistant                          1          879            0.861 
#> 4 ko_ctnnb1 Resistant                          2           62            0.0607
#> # ℹ 3 more variables: mean_prediction <dbl>, predominant_features <chr>,
#> #   distinguishing_features <chr>

The descriptive distinguishing_features and the statistical markers used by plot_explanation_paths() answer related but different questions. The former rank features by the magnitude of between-path mean-SHAP differences, whereas the latter additionally require the per-sample SHAP distributions to pass statistical and effect-size filters.

Inspect training-sample path assignments

The full paths object retains the training-sample assignments used to construct the summaries. These can be helpful for further investigation into the identity of samples clustering to different paths. Retrieve them with get_explanation_path_assignments():

ctnnb1_assignments <- get_explanation_path_assignments(paths, target = ctnnb1_target)
head(ctnnb1_assignments)
#> # A tibble: 6 × 8
#>   target    matrix_row_index sample     prediction_class reconstructed_predict…¹
#>   <chr>                <int> <chr>      <chr>                              <dbl>
#> 1 ko_ctnnb1                5 ACH-000461 Sensitive                          0.932
#> 2 ko_ctnnb1                7 ACH-002023 Sensitive                          0.943
#> 3 ko_ctnnb1               12 ACH-001098 Sensitive                          0.805
#> 4 ko_ctnnb1               15 ACH-000769 Sensitive                          0.892
#> 5 ko_ctnnb1               16 ACH-000421 Sensitive                          0.699
#> 6 ko_ctnnb1               33 ACH-000138 Sensitive                          0.547
#> # ℹ abbreviated name: ¹​reconstructed_prediction
#> # ℹ 3 more variables: explanation_path_id <int>, is_outlier <lgl>,
#> #   assignment_type <chr>

Supported paths are numbered separately within each prediction class. Samples assigned to undersized branches that are retained as outliers have explanation_path_id = 0.

A simple count of the CTNNB1 assignments is:

with(ctnnb1_assignments, table(prediction_class, explanation_path_id, useNA = "ifany"))
#>                 explanation_path_id
#> prediction_class   0   1   2
#>        Resistant  80 879  62
#>        Sensitive  10  89  20

Calling get_explanation_path_assignments(paths) without a target returns assignments for every analyzed target.

When new prediction samples are available in the original fitted models, assign_explanation_paths() assigns each sample to its closest supported training path within the same predicted response class. These assignments are descriptive similarities to fixed training-derived paths; the prediction samples never redefine the path structure.

Interpreting SHAP explanations

The explanation functions in POWERUP operate at different levels but describe the same fitted predictive model. Waterfalls explain individual predictions, SHAP scatters place individual effects within the reference cohort, aggregate contributions summarize recurring model influence across samples, and explanation paths identify recurrent multifeature SHAP patterns within predicted response classes.

None of these analyses establishes that a feature is a causal regulator of the measured response. Explanation paths should likewise not be interpreted automatically as biological subtypes. Their value is in identifying reproducible structures in how the model arrives at its predictions, which can then be considered alongside model performance, prediction uncertainty, experimental evidence, and biological context.

For guidance on prediction uncertainty and other model outputs, see Interpreting POWERUP results. To incorporate experimental measurements into POWERUP priors, continue with Experimental observations and posterior updating.

References

To learn more about the general SHAP framework and its theoretical foundation, see:

Lundberg, S.M. and Lee, S.-I. A Unified Approach to Interpreting Model Predictions. Advances in Neural Information Processing Systems 30 (2017). https://papers.nips.cc/paper/7062-a-unified-approach-to-interpreting-model-predictions

For tree-based models and the TreeSHAP framework used to interpret tree ensembles such as XGBoost, see:

Lundberg, S.M., Erion, G., Chen, H. et al. From local explanations to global understanding with explainable AI for trees. Nature Machine Intelligence 2, 56-67 (2020). https://doi.org/10.1038/s42256-019-0138-9

Session information

sessionInfo()
#> R version 4.4.2 (2024-10-31)
#> Platform: aarch64-apple-darwin20
#> Running under: macOS Sequoia 15.7.3
#> 
#> Matrix products: default
#> BLAS:   /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRblas.0.dylib 
#> LAPACK: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.0
#> 
#> locale:
#> [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
#> 
#> time zone: America/New_York
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] powerup_1.0.95
#> 
#> loaded via a namespace (and not attached):
#>  [1] utf8_1.2.4        sass_0.4.9        future_1.34.0     generics_0.1.3   
#>  [5] tidyr_1.3.1       stringi_1.8.4     lattice_0.22-6    listenv_0.9.1    
#>  [9] digest_0.6.37     magrittr_2.0.3    evaluate_1.0.3    grid_4.4.2       
#> [13] fastmap_1.2.0     xgboost_3.2.1.1   jsonlite_2.0.0    Matrix_1.7-3     
#> [17] mgcv_1.9-1        purrr_1.1.0       viridisLite_0.4.2 scales_1.3.0     
#> [21] codetools_0.2-20  textshaping_1.0.0 jquerylib_0.1.4   cli_3.6.5        
#> [25] rlang_1.1.6       parallelly_1.42.0 splines_4.4.2     cowplot_1.1.3    
#> [29] munsell_0.5.1     withr_3.0.2       cachem_1.1.0      yaml_2.3.10      
#> [33] tools_4.4.2       parallel_4.4.2    dplyr_1.1.4       colorspace_2.1-1 
#> [37] ggplot2_3.5.1     rsample_1.2.1     globals_0.16.3    vctrs_0.6.5      
#> [41] R6_2.6.1          lifecycle_1.0.4   stringr_1.5.2     fs_1.6.5         
#> [45] htmlwidgets_1.6.4 ragg_1.5.1        furrr_0.3.1       pkgconfig_2.0.3  
#> [49] desc_1.4.3        gtable_0.3.6      pkgdown_2.2.0     pillar_1.10.1    
#> [53] bslib_0.9.0       glue_1.8.0        data.table_1.17.0 systemfonts_1.2.2
#> [57] xfun_0.51         tibble_3.3.0      tidyselect_1.2.1  knitr_1.50       
#> [61] farver_2.1.2      nlme_3.1-167      htmltools_0.5.8.1 labeling_0.4.3   
#> [65] rmarkdown_2.29    compiler_4.4.2