R包开发:如何提前加载内部数据并实现参考文件持久化修改?
R包开发:内部参考数据加载与持久化修改问题
我用devtools和roxygen2开发项目专属的数据清洗R包,遵循RStudio规范。多数函数依赖/R目录下sysdata.rda里的参考数据框,用来修正拼写和录入错误,之前引用正常且开启了懒加载。现在想开发add.alt()函数,让用户在会话中给参考文件添加新行,参考内部状态方案把数据存入新环境,但安装包时提示column_standardized不存在,推测是sysdata.rda加载晚于/R下的文件。试过把数据移到/data目录用system.file调用,仍报错。实际参考文件有300多行,无法从头重建。
参考数据示例
column_standardized <- data.frame( standard_name = c("date", "date", "time", "ID", "ID", "ID", "location", "location"), other_names = c("DATE", "day", "TIME", "id", "individual", "name", "LOCATION", "locale") )
环境初始化与add.alt函数代码
the <- new.env(parent = emptyenv()) the$column_standardized <- column_standardized #' Add alternative names to reference #' #' @param correct A character string of the standard/correct name #' @param alt A character string of the alternative name #' @param data_type A character string indicating which reference file to edit. Options are 'column' (and others in reality). #' #' @return NA; edits included reference files for current R session. #' @export add.alt <- function(correct, alt, data_type){ if(data_type == "column"){ key <- the$column_standardized correct_col <- "standard_name" alt_col <- "other_names" }else{print("\nData type not found. Acceptable options are 'column', etc.")} new_key <- data.frame(matrix(ncol = length(colnames(key)), nrow = 1)) colnames(new_key) <- colnames(key) new_key[1,correct_col] <- correct new_key[1, alt_col] <- alt key <- rbind(key, new_key) if(data_type == "column"){ the$column_standardized <- key invisible(key) }}
具体问题
- 有没有办法让系统数据优先加载,让
/R目录下的文件能引用到这些数据?不局限于内部存储(隐私优先),可以移到/data或其他位置,但最初觉得内部存储最简单。 - 能不能实现用户对参考文件的“永久”修改?每次会话调用
add.alt()也能接受,但有更好方案就最好了。
解决方案
问题1:确保参考数据被优先加载
方案1:延迟初始化环境
不在脚本顶层直接赋值the$column_standardized,改为第一次访问时从sysdata.rda加载:
the <- new.env(parent = emptyenv()) #' @importFrom utils data .load_internal_data <- function() { if (!exists("column_standardized", envir = the)) { data("column_standardized", envir = the, package = "your_package_name") } } # 修改add.alt函数,先调用加载逻辑 add.alt <- function(correct, alt, data_type){ .load_internal_data() if(data_type == "column"){ key <- the$column_standardized correct_col <- "standard_name" alt_col <- "other_names" }else{message("\nData type not found. Acceptable options are 'column', etc.")} # 其余代码保持不变 }
第一次调用add.alt()或其他依赖函数时才加载数据,避免顶层加载顺序冲突。
方案2:从包命名空间读取/data目录数据
把column_standardized.rda放到/data目录,通过包命名空间直接获取:
the <- new.env(parent = emptyenv()) .load_data <- function() { if (!exists("column_standardized", envir = the)) { the$column_standardized <- get("column_standardized", envir = asNamespace("your_package_name")) } }
这种方式无需用户手动加载数据,包内部可直接访问,解决加载顺序问题。
问题2:实现持久化修改
要让修改跨会话保存,需将修改后的数据写入用户本地文件,加载包时优先读取本地修改版:
步骤1:定义用户数据存储路径
.user_data_path <- function() { file.path(rappdirs::user_data_dir("your_package_name"), "column_standardized.rda") }
需在DESCRIPTION中添加Imports: rappdirs,用rappdirs获取跨平台的用户数据目录。
步骤2:修改加载逻辑,优先读取本地数据
.load_internal_data <- function() { user_path <- .user_data_path() # 优先加载用户本地修改后的文件 if (file.exists(user_path)) { load(user_path, envir = the) } else { # 无本地文件时加载包自带的原始数据 data("column_standardized", envir = the, package = "your_package_name") } }
步骤3:修改add.alt函数,保存修改到本地
add.alt <- function(correct, alt, data_type){ .load_internal_data() if(data_type == "column"){ key <- the$column_standardized correct_col <- "standard_name" alt_col <- "other_names" }else{message("\nData type not found. Acceptable options are 'column', etc."); return(invisible(NULL))} # 避免重复添加相同映射 if (!any(key[[correct_col]] == correct & key[[alt_col]] == alt)) { new_key <- data.frame( standard_name = correct, other_names = alt, stringsAsFactors = FALSE ) key <- rbind(key, new_key) the$column_standardized <- key # 创建用户数据目录(不存在则创建) dir.create(dirname(.user_data_path()), recursive = TRUE, showWarnings = FALSE) # 保存修改后的数据到本地 save(column_standardized, file = .user_data_path(), envir = the) } invisible(key) }
用户添加的新映射会保存在本地,下次启动R会话加载包时自动加载修改后的数据。
额外建议
- 给
add.alt()添加重复检查,避免冗余数据。 - 在包的启动脚本
zzz.R中调用.load_internal_data(),确保数据在包加载时就绪:.onLoad <- function(libname, pkgname) { .load_internal_data() } - 创建数据框时添加
stringsAsFactors = FALSE,避免因子类型引发的问题。 - 测试安装包时用
devtools::install()代替load_all(),模拟真实安装环境的加载顺序。
内容的提问来源于stack exchange,提问作者Becca Supple
相关产品推荐
相关产品推荐

