R包开发自定义函数传参调用lubridate处理时间变量报错问题
R包开发中tidyverse函数的列名参数传递问题解决
报错原因分析
1. 不带引号传入列名报错
你直接传入未加引号的Time、Day作为参数时,R会优先在当前全局环境中查找名为Time、Day的对象,找不到就抛出object not found错误。这是因为tidyverse的非标准评估默认不会直接在函数参数层面自动将未定义的变量识别为数据框的列名,需要特殊语法处理。
2. 字符串形式传入列名报错
你传入字符串参数时,管道中的dplyr、tidyr函数会直接读取参数的字符串值,而不会把它解析为数据框的列名。比如你传入time_var = "Time",代码中lubridate::hour(as.character(time_var))实际是对"Time"这个固定字符串做处理,自然会报格式错误。
修复方案
你可以借助tidyverse的rlang工具链处理动态列名,这里提供支持裸列名、字符串列名两种传参方式的修改方案:
library(rlang) # 包开发时可直接在Imports中添加rlang read_tag <- function(tagfile, type, time_var = NULL, date_var, delim = ",") { # 统一将传入的参数转为字符串列名,兼容裸名和字符串两种传入方式 date_var <- as.character(ensym(date_var)) if (!is.null(time_var)) { time_var <- as.character(ensym(time_var)) } if (type == "argos") { data <- readr::read_delim(file = tagfile, col_names = TRUE, delim = delim) data <- data %>% # 用all_of()包裹字符串列名传入tidyr函数 tidyr::separate(all_of(date_var), c("Day", "Month", "Year"), sep = "-") %>% # 用.data[[列名字符串]]动态取dplyr中的列 dplyr::mutate( hour = lubridate::hour(.data[[time_var]]), minutes = lubridate::minute(.data[[time_var]]) ) %>% dplyr::mutate(date = lubridate::ymd(paste(Year, Month, Day, sep = "-"))) %>% dplyr::mutate(date_time = lubridate::ymd_hms(paste(date, .data[[time_var]], sep = " "))) %>% dplyr::mutate(year_day = lubridate::yday(date)) } else if (type == "internal") { data <- readr::read_delim(file = tagfile, col_names = TRUE, col_types = readr::cols(), delim = delim, quote = "#") data[[date_var]] <- as.POSIXct(x = data[[date_var]], origin = "1970-01-01") data <- dplyr::mutate(data, year = lubridate::year(.data[[date_var]]), month = lubridate::month(.data[[date_var]], label = TRUE), day = lubridate::day(.data[[date_var]]), hour = lubridate::hour(.data[[date_var]]), minutes = lubridate::minute(.data[[date_var]]), seconds = lubridate::second(.data[[date_var]]), week = lubridate::week(.data[[date_var]]), day_number = lubridate::yday(.data[[date_var]]), dn = ifelse(hour >= 9 & hour < 21, "Day", "Night")) } return(data) }
注:原函数缺少显式返回值,运行后不会输出结果,修复代码中已补充返回逻辑;同时修正了日期时间拼接时无分隔符的问题,避免解析失败
调用测试
修改完成后两种传参方式都可以正常运行:
# 裸列名传参 read_tag(tagfile = "path/to/file.csv", type = "argos", time_var = Time, date_var = Day, delim = ",") # 字符串传参 read_tag(tagfile = "path/to/file.csv", type = "argos", time_var = "Time", date_var = "Day", delim = ",")
内容的提问来源于stack exchange,提问作者Daniel Estévez
相关产品推荐
相关产品推荐

