R包开发:嵌套Tidyeval函数为何重复用!!x及函数整合问题
解决R包中整合日期转换与组件提取函数的问题
为什么你仍然需要使用!!x?
你疑惑的点在于,既然date_from_text已经处理了x列,为什么后续mutate里还要用!!x。其实原因很简单:
rlang::enquo(x)捕获的是列名的符号(比如当你传入time时,它捕获的是quote(time)),而不是列本身的数据。date_from_text的作用是修改数据框中该列的类型(从字符转成日期时间),但列名并没有改变。- 在
dplyr::mutate中,!!x是把这个符号解引用,指向数据框中对应的列——不管这个列的类型有没有被修改,只要列名没变,!!x就依然有效。
所以你之前的代码里用!!x是正确的,但问题在于你还依赖外部的date_from_text函数,而你不想让它出现在包的NAMESPACE里。
整合函数并添加类型判断的正确写法
我们可以把date_from_text的逻辑直接内嵌到date_columns中,同时添加类型检查:只有当列不是日期时间类型时,才进行转换。这样就不需要单独的date_from_text函数了,自然也不会暴露到NAMESPACE中。
修改后的代码如下:
date_columns <- function(df, x) { # 捕获传入的列符号 x <- rlang::enquo(x) # 获取列名(用于后续判断和修改) col_name <- rlang::quo_name(x) # 检查列是否已经是日期时间类型 if (!inherits(dplyr::pull(df, !!x), c("POSIXct", "POSIXlt"))) { # 如果不是,转换为ymd_hms格式的日期时间 df <- df %>% dplyr::mutate(!!col_name := lubridate::ymd_hms(!!x)) } # 提取各种日期时间组件 df %>% dplyr::mutate( year = lubridate::year(!!x), ydag = lubridate::yday(!!x), weekday = lubridate::wday(!!x, label = FALSE), hour = lubridate::hour(!!x), hour_min = hms::as.hms(lubridate::force_tz(!!x)), week_num = lubridate::week(!!x), month = lubridate::month(!!x), date = lubridate::date(!!x) ) }
代码说明
- 类型检查:用
inherits()判断列是否属于POSIXct或POSIXlt(R中标准的日期时间类型),避免重复转换已经是日期类型的列。 - 内嵌转换逻辑:把原来
date_from_text的转换代码直接写到date_columns里,不需要外部函数。 - 符号解引用:全程用
!!x引用目标列,因为列名始终没变,只是类型可能被修改了。
测试验证
用你提供的测试数据验证:
df <- structure(list(time = c("2018-01-30 20:08:18", "2018-02-01 21:01:25", "2018-01-31 23:25:12", "2018-01-28 23:45:34", "2018-01-31 12:00:55", "2018-02-04 09:15:31", "2018-01-27 21:08:02", "2018-02-08 01:50:31", "2018-02-01 03:15:43", "2018-02-04 01:04:52"), type = c("A", "D", "B", "B", "B", "D", "C", "C", "C", "A")), .Names = c("time", "type"), row.names = c(NA, -10L), class = c("tbl_df", "tbl", "data.frame")) # 调用函数 result <- date_columns(df, time) # 查看结果 head(result)
运行后,time列会被转换成POSIXct类型,同时新增所有日期组件列,完全符合你的需求。
内容的提问来源于stack exchange,提问作者xhr489
相关产品推荐
相关产品推荐

