【问题标题】:lubridate convert decimals into monthslubridate 将小数转换为月份
【发布时间】:2018-03-27 10:24:10
【问题描述】:

我使用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 天的近似值也会更完美,但除了冗长的计算,我不知道该怎么做。

最后一个想法是使用本文开头所述的计算来计算年份和月份,然后使用类似于make_date(但看起来像@987654333 @ 似乎还不存在)。

整个过程对我来说看起来很麻烦,有人有不同的看法吗?

非常感谢

马诺洛

【问题讨论】:

    标签: r decimal duration lubridate


    【解决方案1】:

    虽然 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"
    

    更新

    如果要返回整数年、月和小数日,可以将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
    

    【讨论】:

    • 谢谢,这与我正在寻找的非常接近。 (它也给了我一个很好的函数例子,我还在学习)。我有两个问题,如果你不介意的话。首先是为什么在返回行中,天有%3.2f 而不是%i 作为年和月?其次,鉴于输出是类字符条目的列表,您是否认为如果我省略日期并只关注年份和月份,我应该能够计算频率(我有大约 35,000 个观察值)?
    • @Manolo 你的日子是分数,所以"%3.2f" 打印一个带有两位有效数字的分数。另一方面,年和月是整数,因此"%i"。我不确定您在第二个问题中的意思;您可以让函数返回整数年、月和小数天,请参阅我更新的答案。然后,您可以轻松提取数字以进行进一步处理。
    • 亲爱的@maurits-evers 和@g-grothendieck,谢谢你们的有效建议。经过一番思考,我得出的结论是,首先使用十进制值执行所有分析会容易得多,然后使用@maurits-evers 函数将小数转换为使用unlist(lapply(age, f)) 的字符,但仅适用于演示目的(例如,在摘要或列联表中,读者应该更容易理解 15 年零 5 个月而不是 15.43 年)
    【解决方案2】:

    我们可以定义自己的ym S3 类来表示年/月对象。在这里,我们定义了几个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 类。

    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"
    

    【讨论】:

    • 谢谢,如果我理解得很好,要获得一些统计数据(比如mediancountquartiles),我是否需要为每个函数定义函数(例如median.ymcount.ym,等等)?情节也一样?
    • S3 将免费为您提供其中的许多功能,如前所述,您需要进行实验,即尝试一下。例如,median(as.ym(x)) 有效,但以年为单位给出结果。如果几年的结果还可以,那么您可以按原样使用它。如果没有,您可以使用as.ym(median(as.ym(x))) 或定义一个median.ym 方法,它执行相同的操作,等等。
    猜你喜欢
    • 2023-03-12
    • 2014-03-30
    • 1970-01-01
    • 1970-01-01
    • 2020-07-07
    • 2021-03-03
    • 1970-01-01
    • 2010-09-16
    • 1970-01-01
    相关资源
    最近更新 更多