R语言如何提取传入函数的嵌套列表单个元素对应名称
我通过.yml文件加载了多个指向不同数据框的文件路径,这些路径以命名列表的结构存储,以下是极简示例:
settings_list <- list(survey = list(df1 = "path", df2 = "path2", df3 = "path3", df4 = "path4"))
该列表的结构如下:
> str(settings_list) List of 1 $ survey:List of 3 ..$ df1: chr "path" ..$ df2: chr "path2" ..$ df3: chr "path3" ..$ df4: chr "path4"
由于各个数据框的结构差异较小,我编写了一个可接收单个数据框路径并完成数据清洗的函数,调用示例如下:
foo_cleaning(path = settings_list$survey$df3)
后续我会使用purrr::map()遍历settings_list$survey下的所有元素完成批量清洗。
我希望为该函数新增可选功能:将清洗完成的数据框以对应列表元素的名称作为对象名,保存到全局环境中(例如上述调用需生成名为df3的对象)。我知晓可以直接将所有清洗结果存储在列表中,但出于其他实际需求,我仍需要在函数中实现该功能。
目前该功能的主体代码已经编写完成,但我遇到了一个问题:无法从函数输入中提取到正确的元素名称。
names(settings_list$survey$df3) NULL #期望输出: "df3"
对列表的更高层级调用names()会返回全部元素的名称向量,但我需要仅提取settings_list$survey$df3对应的单个名称:
names(settings_list$survey) [1] "df1" "df2" "df3" "df4"
除此之外,我也想了解是否可以将函数输入的表达式settings_list$survey$df3转换为字符串,从而通过正则表达式提取最后一段的元素名?
方案1:适配purrr批量遍历场景(推荐)
不需要在函数内部解析输入表达式,使用purrr遍历的时候本身就可以直接拿到元素名,是稳定性最高、边界问题最少的实现方式。purrr的imap()/iwalk()函数会自动把列表元素的名称作为第二个参数传入处理函数,完全不会出现名称提取错误的问题:
library(purrr) # 改写你的清洗函数,增加可选参数控制是否写入全局环境 foo_cleaning <- function(path, obj_name = NULL, save_to_global = FALSE) { # 此处替换为你原有的数据清洗逻辑 cleaned_df <- read.csv(path) # 全局环境写入逻辑 if (isTRUE(save_to_global) && !is.null(obj_name)) { assign(x = obj_name, value = cleaned_df, envir = .GlobalEnv) } # 保留原有返回值,不影响将结果存入列表的需求 return(cleaned_df) } # 批量遍历,自动传入元素名 iwalk( .x = settings_list$survey, .f = ~foo_cleaning(path = .x, obj_name = .y, save_to_global = TRUE) )
运行完成后全局环境会直接生成df1、df2、df3、df4四个对应的数据框对象,如果需要同时获取列表格式的清洗结果,把iwalk()替换为imap()并将结果赋值给变量即可,两种需求可以同时满足。
方案2:单函数调用场景的表达式解析
如果你需要支持单独调用foo_cleaning(path = settings_list$survey$df3)时自动识别元素名,可以通过非标准计算捕获输入表达式,转成字符串后用正则提取最后一级的名称:
foo_cleaning <- function(path, save_to_global = FALSE) { # 捕获传入的path参数表达式,转为字符串 path_expr <- deparse(substitute(path)) # 正则提取最后一个$符号后的内容作为对象名 obj_name <- sub(".*\\$", "", path_expr) # 此处替换为你原有的数据清洗逻辑 cleaned_df <- read.csv(path) if (isTRUE(save_to_global)) { assign(x = obj_name, value = cleaned_df, envir = .GlobalEnv) } return(cleaned_df) } # 单独调用测试,会自动在全局环境生成df3对象 foo_cleaning(path = settings_list$survey$df3, save_to_global = TRUE)
注意:该方案存在使用限制,如果你先把路径赋值给独立变量再传入函数(比如
p <- settings_list$survey$df3后调用foo_cleaning(path = p)),正则提取会拿到变量名p而非预期的df3,批量处理场景下优先选择方案1。
内容的提问来源于stack exchange,提问作者Rasul89

