R语言:包开发中矩阵列自定义基扩展的嵌套函数实现难题
Hey there! Let’s break down your two R questions step by step—both are super common when working with functional programming and package development, so I totally get where you’re stuck.
1. Passing a Function as the FUN Argument in lapply
First off, lapply is built to accept functions directly as its FUN parameter—you just need to know a few common patterns depending on your use case:
Pass a named function directly: If your function doesn’t need extra parameters beyond the elements you’re iterating over, just use the function name. For example:
# Define a simple custom function square <- function(x) x^2 # Apply it to each element of a list lapply(list(1:3, 4:6), square)Pass a function with extra arguments: Use the
...parameter inlapplyto pass additional arguments to your function. For example, usingmeanwithna.rm = TRUE:lapply(list(c(1, NA, 3), c(NA, 5, 6)), mean, na.rm = TRUE)Use an anonymous function: For one-off logic you don’t want to formalize into a named function, write an inline anonymous function:
lapply(list(1:3, 4:6), function(x) sum(x) + 10)Pass functions from other packages: Just reference them with their namespace (if needed) directly. For example, using
splines::bs:library(splines) lapply(list(1:10, 11:20), bs, df = 3)
2. Supporting Custom Column-Wise Expansion Functions in a Package
I suspect your issue with eval and substitute comes down to scope problems or mismanaging how the custom function and its arguments are passed. Let’s skip the messy eval/substitute dance (unless you specifically need to capture expression metadata) and use more reliable approaches:
Method 1: lapply + do.call (Simple & Reliable)
The core idea is to convert your matrix into a list of columns, apply the user’s expansion function to each column (with their specified arguments), then combine the results back into a matrix. Here’s a package-ready function:
expand_matrix <- function(mat, expand_fun, ...) { # Convert matrix to a list of columns col_list <- as.list(data.frame(mat)) # Apply the expansion function to each column, passing extra args expanded_cols <- lapply(col_list, function(col) { do.call(expand_fun, args = list(x = col, ...)) }) # Combine expanded columns into a single matrix do.call(cbind, expanded_cols) }
Test it with your example data:
set.seed(123) mat <- replicate(4, rnorm(10)) # Use splines::bs with df=3 library(splines) expanded_mat <- expand_matrix(mat, expand_fun = bs, df = 3) dim(expanded_mat) # Should return 10 rows, 12 columns (4 cols × 3 df)
Method 2: Safe Function Handling with match.fun
If you want to handle cases where users might pass a function name as a string (like "bs" instead of bs), use match.fun—it’s a super reliable tool for converting function names/strings into valid function objects:
expand_matrix_robust <- function(mat, expand_fun, ...) { # Safely resolve the function object (works for names, strings, or namespace references) fun_obj <- match.fun(expand_fun) col_list <- as.list(data.frame(mat)) expanded_cols <- lapply(col_list, function(col) { res <- do.call(fun_obj, args = list(x = col, ...)) # Ensure output is a matrix (handles cases where functions return vectors) if (!is.matrix(res)) res <- as.matrix(res) res }) do.call(cbind, expanded_cols) } # This works too! expanded_mat2 <- expand_matrix_robust(mat, "splines::bs", df = 3) all.equal(expanded_mat, expanded_mat2) # Returns TRUE
Why Your eval/substitute Approach Might Have Failed
Common pitfalls here include:
- Namespace issues: If you’re evaluating the function in your package’s namespace instead of the user’s environment, functions like
splines::bsmight not be found. Always specifyenvir = parent.frame()if you must useeval. - Missing argument mapping: Most expansion functions (like
bs,poly) expect the input vector as the first argumentx—if you didn’t explicitly pass the column tox, the function would throw an error. - Mixed output types: If some expansions return vectors and others return matrices, merging with
cbindcan break—hence theas.matrixcheck in the robust version.
内容的提问来源于stack exchange,提问作者sahir

