Skip to content

Commit c4efd91

Browse files
committed
Add RecordArgs function + implement it in Prep4HPC and PrepData functions
1 parent bdb65a0 commit c4efd91

File tree

4 files changed

+181
-3
lines changed

4 files changed

+181
-3
lines changed

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export(Predict_Maps)
112112
export(Railway_Intensity)
113113
export(Range2NewVal)
114114
export(RastPA)
115+
export(RecordArgs)
115116
export(ReloadPackage)
116117
export(Rename_geometry)
117118
export(ReplaceSpace)

R/General_RecordArgs.R

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
#' Capture and record function arguments
2+
#'
3+
#' `RecordArgs()` is a utility function designed to capture and record both the
4+
#' evaluated and unevaluated forms of arguments passed to a parent function. It
5+
#' returns a `tibble` where each argument is represented by two columns: one for
6+
#' the unevaluated expression (suffix `_orig`) and one for the evaluated value
7+
#' (suffix `_eval`). The function dynamically handles scalars, call objects, and
8+
#' complex objects (e.g., `lm` models, `SpatRaster` objects), preserving their
9+
#' structure appropriately.
10+
#' @return A tibble containing the unevaluated and evaluated forms of the parent
11+
#' function’s arguments, with columns named using the argument names followed
12+
#' by `_orig` and `_eval` suffixes. if `ExportPath` is `NULL` (default), the
13+
#' tibble is returned; otherwise, it is saved as an `.RData` file and returns
14+
#' `NULL`.
15+
#' @author Ahmed El-Gabbas
16+
#' @name RecordArgs
17+
#' @export
18+
#' @param ExportPath Character. The path of an `RData` file to export the tibble
19+
#' to . If `NULL` (default), the tibble will not be saved to disk, but will be
20+
#' returned as an object.
21+
#' @details This function must be called from within another function. It uses
22+
#' `sys.call(-1)` to capture the parent function’s call, evaluates arguments
23+
#' in the parent environment, and combines them with default values from the
24+
#' parent function’s formal arguments. Unevaluated expressions (e.g., `a + b`)
25+
#' are converted to character strings, while evaluated values are kept as-is
26+
#' for scalars or wrapped in lists for complex objects. Columns are ordered
27+
#' based on the original argument sequence in the parent function’s
28+
#' definition, with unevaluated variants preceding evaluated variants (e.g.,
29+
#' `x_orig`, `x_eval`).
30+
#' @examples
31+
#' # Basic usage with scalars and expressions
32+
#' a <- 5
33+
#' b <- 3
34+
#' Fun1 <- function(w = 5, x, y, z = 10) {
35+
#' Args <- RecordArgs()
36+
#' return(Args)
37+
#' }
38+
#' AA <- Fun1(x = a + b, y = 2)
39+
#' AA$w_orig # 5
40+
#' AA$w_eval # 5
41+
#' AA$x_orig # "a + b"
42+
#' AA$x_eval # 8
43+
#' AA$y_orig # 2
44+
#' AA$y_eval # 2
45+
#' AA$z_orig # 10
46+
#' AA$z_eval # 10
47+
#'
48+
#' # Usage with complex objects (lm and SpatRaster)
49+
#' AA <- Fun1(
50+
#' w = 10,
51+
#' x = a + b,
52+
#' y = stats::lm(mpg ~ disp + hp, data = mtcars),
53+
#' z = terra::rast(system.file("ex/logo.tif", package = "terra")))
54+
#' AA$w_orig # 10
55+
#' AA$w_eval # 10
56+
#' AA$x_orig # "a + b"
57+
#' AA$x_eval # 8
58+
#' AA$y_orig # "lm(mpg ~ disp + hp, data = mtcars)"
59+
#' AA$y_eval[[1]] # lm object
60+
#' AA$z_orig # "terra::rast(system.file("ex/logo.tif", package = "terra"))"
61+
#' AA$z_eval[[1]] # SpatRaster object
62+
63+
RecordArgs <- function(ExportPath = NULL) {
64+
65+
# Get the call to the parent function (one level up)
66+
call_info <- sys.call(-1)
67+
68+
if (is.null(call_info)) {
69+
stop(
70+
"RecordArgs() must be called from within another function",
71+
call. = FALSE)
72+
}
73+
74+
# Extract the arguments, excluding the function name
75+
args_list <- as.list(call_info)[-1]
76+
77+
# Extract the name of the calling function
78+
calling_func <- deparse(call_info[[1]])
79+
80+
# Get the parent function's environment and formal arguments
81+
parent_env <- parent.frame()
82+
parent_func <- sys.function(-1)
83+
formals_full <- formals(parent_func)
84+
85+
# Initialize results
86+
Evaluated <- NULL
87+
Unevaluated <- NULL
88+
89+
# Evaluate the arguments in the parent environment
90+
args_values <- lapply(args_list, eval, envir = parent_env)
91+
recorded_values <- stats::setNames(args_values, names(args_list))
92+
# Combine with default values
93+
Evaluated <- modifyList(formals_full, recorded_values)
94+
# Store unevaluated expressions
95+
Unevaluated <- modifyList(formals_full, args_list)
96+
97+
# Get argument names in their original order - use formal argument order
98+
arg_names <- names(formals_full)
99+
100+
# Construct column names
101+
eval_cols <- paste0(arg_names, "_eval")
102+
uneval_cols <- paste0(arg_names, "_orig")
103+
104+
# Prepare Evaluated values: keep scalars as-is, wrap complex objects in lists
105+
eval_values <- purrr::map(
106+
.x = as.list(Evaluated),
107+
.f = function(x) {
108+
if (is.vector(x) && length(x) == 1 && !is.list(x)) {
109+
# Scalars (numeric, character) stay as-is
110+
return(x)
111+
} else {
112+
# Complex objects (e.g., lm, SpatRaster) wrapped in list
113+
114+
if (inherits(x, "SpatRaster")) {
115+
x <- terra::wrap(x)
116+
}
117+
118+
return(list(x))
119+
}
120+
})
121+
122+
# Prepare Unevaluated values: keep calls as-is, wrap in list, scalars stay
123+
# as-is
124+
uneval_values <- purrr::map(
125+
.x = as.list(Unevaluated),
126+
.f = function(x) {
127+
if (is.call(x)) {
128+
# Keep call objects as-is, wrapped in list
129+
return(list(x))
130+
} else if (is.vector(x) && length(x) == 1 && !is.list(x)) {
131+
# Scalars stay as-is
132+
return(x)
133+
} else {
134+
# Other objects wrapped in list
135+
if (inherits(x, "SpatRaster")) {
136+
x <- terra::wrap(x)
137+
}
138+
return(list(x))
139+
}
140+
})
141+
142+
# Combine into a tibble
143+
tibble_data <- c(
144+
# Unevaluated first
145+
stats::setNames(uneval_values, uneval_cols),
146+
# Evaluated second
147+
stats::setNames(eval_values, eval_cols))
148+
149+
# Create tibble and reorder columns by argument order
150+
result <- tibble::as_tibble(tibble_data)
151+
152+
# Define the desired column order: unevaluated before evaluated for each arg
153+
desired_order <- lapply(
154+
arg_names,
155+
function(n) {
156+
c(paste0(n, "_orig"), paste0(n, "_eval"))
157+
}) %>%
158+
unlist()
159+
160+
result <- dplyr::select(result, tidyselect::all_of(desired_order))
161+
162+
# Export the tibble to a file if a path is provided
163+
if (is.null(ExportPath)) {
164+
return(result)
165+
} else {
166+
IASDT.R::SaveAs(
167+
InObj = result, OutObj = paste0(calling_func, "_args"),
168+
OutPath = ExportPath)
169+
return(invisible(NULL))
170+
}
171+
172+
173+
}

R/Mod_Prep4HPC.R

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
#' (e.g., CHELSA Bioclimatic variables, habitat coverage, road and railway
2020
#' intensity, sampling efforts) into a single dataset. Processed data is saved
2121
#' to disk as an `*.RData` file.
22-
#' @param DirName Character. Directory name, without its parents, where the
22+
#' @param DirName Character. Directory name, without its parents, where the
2323
#' models will be saved. This directory will be created.
2424
#' @param GPP Logical. Whether to fit spatial random effect using Gaussian
2525
#' Predictive Process. Defaults to `TRUE`. If `FALSE`, non-spatial models will
@@ -311,6 +311,9 @@ Mod_Prep4HPC <- function(
311311
call. = FALSE)
312312
}
313313

314+
IASDT.R::RecordArgs(
315+
ExportPath = IASDT.R::Path(Path_Model, "Args_Prep4HPC.RData"))
316+
314317
# # ..................................................................... ###
315318

316319
# # |||||||||||||||||||||||||||||||||||
@@ -375,7 +378,6 @@ Mod_Prep4HPC <- function(
375378
if (!all(is.numeric(GPP_Dists)) || any(GPP_Dists <= 0)) {
376379
stop("`GPP_Dists` should be numeric and greater than zero", call. = FALSE)
377380
}
378-
379381
}
380382

381383
# # ..................................................................... ###

R/Mod_PrepData.R

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ Mod_PrepData <- function(
6868
call. = FALSE)
6969
}
7070

71-
7271
# # ..................................................................... ###
7372

7473
# # |||||||||||||||||||||||||||||||||||
@@ -102,6 +101,9 @@ Mod_PrepData <- function(
102101
}
103102
fs::dir_create(Path_Model)
104103

104+
IASDT.R::RecordArgs(
105+
ExportPath = IASDT.R::Path(Path_Model, "Args_PrepData.RData"))
106+
105107
# # ..................................................................... ###
106108

107109
# # |||||||||||||||||||||||||||||||||||

0 commit comments

Comments
 (0)