lubridate 将小数转换为月份

lubridate convert decimals into months

我使用 lubridate 通过从出生日期 wdob 中减去结婚日期 wdow 来估计变量 age.first.union 作为时差。我得到以下数字向量

head(wm$age.first.union, 3)
[1] 15.43014 12.67123 17.34247

我想将小数转换成月(也可能转换成天,但这是次要细节),所以第一个值是 15 年零 5 个月。我所做的是创建一系列新变量,然后执行一些计算。为了获得月数,首先,我复制并截断了 age.first.union 变量。然后我估计两者之间的差异以仅获得小数部分,然后使用比例(例如 0.43 : 10 = x : 12 )来获得月份。

我查看了 lubridate 文档,但找不到太多相关信息。我尝试了以下

years(floor(dseconds(15.43014)))

但我只有年份

[1] "15y 0m 0d 0H 0M 0S"

一个想法是以秒为单位获取持续时间

seconds(floor(dyears(15.43014)))
[1] "486604895S"

但接下来的挑战是月份的长度不同。即使是年=365天,月=30天的近似值也会更完美,但除了冗长的计算,我不知道如何去做。

最后一个想法是使用本文开头所述的计算方法计算年和月 post,然后使用类似于 make_date 的方法将这两个变量合并到最后一个变量中(但看起来 make_duration 似乎还不存在)。

整个过程看起来很繁琐,谁有不同的看法?

非常感谢

马诺洛

虽然 lubridate 提供了一个函数 decimal_date 将小数 date 转换为 D-M-Y date,您似乎正在处理 durations。所以这行不通。

但是,您可以很容易地定义一个自定义函数来提取整数年、月和小数日(基于常规年份每月平均 30.42 天):

age <- c(15.43014 12.67123 17.34247)


f <- function(x) {
    year <- floor(x);
    month <- floor((x - year) * 12);
    day <- ((x - year) * 12 - month) * 30.42;
    return(sprintf("%i years, %i months, %3.2f days", year, month, day))
}

lapply(age, f);
#[[1]]
#[1] "15 years, 5 months, 4.92 days"
#
#[[2]]
#[1] "12 years, 8 months, 1.67 days"
#
#[[3]]
#[1] "17 years, 4 months, 3.34 days"

更新

如果你想return整数年、月和小数日你可以定义f

f <- function(x) {
    year <- floor(x);
    month <- floor((x - year) * 12);
    day <- ((x - year) * 12 - month) * 30.42;
    return(list(year = year, month = month, day = day))
}

这给你例如

sapply(age, f);
#      [,1]     [,2]     [,3]
#year  15       12       17
#month 5        8        4
#day   4.918306 1.665799 3.335249

我们可以定义自己的ym S3 class来表示year/month个对象。这里我们定义了几个 ym 方法以及年和月的提取函数。 as.data.frame.ym 方法是部分实现。我们将一个月定义为一年的 1/12。

as.ym <- function(x, ...) structure(x, class = "ym")
as.data.frame.ym <- function(x, ...) 
  structure(list(x), row.names = seq_along(x), class = "data.frame")
years.ym <- as.integer
months.ym <- function(x) 12 * as.numeric(x) %% 1
format.ym <- function(x, ...) paste0(years.ym(x), "Y ", round(months.ym(x)), "M")
print.ym <- function(x, ...) print(format(x), ...)

# test

x <- c(15.43014, 12.67123, 17.34247) # test input

xx <- as.ym(x)
xx
## [1] "15Y 5M" "12Y 8M" "17Y 4M"

DF <- data.frame(x, xx)
DF
         x     xx
1 15.43014 15Y 5M
2 12.67123 12Y 8M
3 17.34247 17Y 4M

years.ym(xx)
## [1] 15 12 17

months.ym(xx)
## [1] 5.16168 8.05476 4.10964

class(xx)
## [1] "ym"

为了将天数也包括在内,我们假设一年有 365.25 天,并且我们再次使用一年中的 12 个月。我们为此创建了一个 ymd S3 class。

as.ymd <- function(x, ...) structure(x, class = "ymd")
as.data.frame.ymd <- function(x, ...) 
  structure(list(x), row.names = seq_along(x), class = "data.frame")
years.ymd <- as.integer
months.ymd <- function(x) as.integer(12 * as.numeric(x) %% 1)
days.ymd <- function(x) (365.25 * as.numeric(x)) %% (365.25 / 12)
format.ymd <- function(x, ...) 
 paste0(years.ymd(x), "Y ", as.integer(months.ymd(x)), "M ", round(days.ymd(x), 1), "D")
print.ymd <- function(x, ...) print(format(x), ...)

xx <- as.ymd(x)
xx
## [1] "15Y 5M 4.9D" "12Y 8M 1.7D" "17Y 4M 3.3D"

DF <- data.frame(x, xx)
DF
         x          xx
1 15.43014 15Y 5M 4.9D
2 12.67123 12Y 8M 1.7D
3 17.34247 17Y 4M 3.3D

years.ymd(xx)
## [1] 15 12 17

months.ymd(xx)
## [1] 5 8 4

days.ymd(xx)
## [1] 4.921135 1.666758 3.337167

class(xx)
## [1] "ymd"