如何使用小数转换年,月,日的年龄

时间:2017-11-23 22:59:23

标签: r date decimal

假设我有一个字符串

   age<-c("7y2m4d","5m4d","7y5m6d")

我想将其转换为像

这样的数字向量
  c(7.34, 0.43, 7.43)

如何制作R代码?

我们可以假设一年有365天,一个月有365/12天。

3 个答案:

答案 0 :(得分:3)

lubridate::duration会将您的字符串转换为(近似)秒。

library(lubridate)
library(magrittr)
age <- c("7y2m4d", "5m4d", "7y5m6d")

age_sec <- age %>%
  duration() %>%
  as.numeric()

age_sec
[1] 226508400  13494600 234570600

然后您可以将年份近似为365 * 24 * 60 * 60秒:

age_sec / (365 * 24 * 60 * 60)
[1] 7.182534 0.427911 7.438185

答案 1 :(得分:2)

基础R的另一种解决方案:

age<-c("7y2m4d","5m4d","7y5m6d")


age <- gsub('y', ' + ', age)
age <- gsub('m', ' / 12 + ', age)
age <- gsub('d', ' / 365', age)

sapply(age, function(x) eval(parse(text = x)))
#7 + 2 / 12 + 4 / 365     5 / 12 + 4 / 365 7 + 5 / 12 + 6 / 365 
#           7.1776256            0.4276256            7.4331050 

我们的想法是创建公式,然后为矢量的每个元素评估它。

答案 2 :(得分:2)

这些解决方案:

  • 处理缺少y,m和/或d和
  • 给出与问题相同的答案(age的第一个元素除外,问题似乎是错误地计算了答案)
  • 避免使用eval
  • 仅使用基地(替代1a除外)

基于简单性(1a)比较下面的解决方案是最简单的并自动处理所有边缘情况,没有特定的代码,表明它是最自然的;但是,它确实使用了一个包。 (1)只是略微复杂,不使用包装,(2)非常短,也不使用任何包装,但不像(1)或(1a)那么简单。

1)此处getNum提取并返回与代码相关联的x的数字(代码为“y”,“m”或“d”)或如果x中没有代码,则返回0.然后我们将年,月/ 12和日/ 365加起来。

getNum <- function(code, x) {
  pat <- sprintf(".*?(\\d+)%s.*", code)
  as.numeric(ifelse(grepl(code, x), sub(pat, "\\1", x), 0))
}
getNum("y", age) + getNum("m", age) / 12 + getNum("d", age) / 365
## [1] 7.1776256 0.4276256 7.4331050

1a)这类似于(1),除了我们在gsubfn中使用strapply来简化getNum。实际上,getNum缩减为单个strapply调用,并且它使用的正则表达式也更简单。

library(gsubfn)

 getNum <- function(code, x) {
   strapply(x, paste0("(\\d+)", code), as.numeric, empty = 0, simplify = TRUE)
 }
getNum("y", age) + getNum("m", age) / 12 + getNum("d", age) / 365
## [1] 7.1776256 0.4276256 7.4331050

2)此替代方法将每个字符串转换为dcf格式,并使用read.dcf创建y,m和d数字的矩阵。

详细地说,第一行代码是处理某些边缘情况,这些边缘情况实际上不存在于问题中的样本数据中。如果d缺失,我们首先将0d附加到age(来自问题),以便我们可以处理y,m和d都缺失的情况。我们还预先添加一个虚拟条目,以确保y,m和d出现在至少一个条目中。如果我们知道y,m和d存在于至少一个组件中并且没有组件中y,m和d都同时缺失,则可以省略第一行代码。

第二行代码将每个输入字符串转换为dcf格式并将其读入矩阵,确保列按已知顺序删除上面添加的虚拟条目。

最后,我们将N替换为0并使用矩阵乘法来累计年,月/ 12和日/ 365.

a0 <- c("0y0m0d", paste0(age, ifelse(grepl("d", age), "", "0d")))
m <- read.dcf(textConnection(gsub("(\\d+)(\\D)", "\\2: \\1\n", a0)))[-1, c("y", "m", "d")]
m[is.na(m)] <- 0
c(array(as.numeric(m), dim(m)) %*% c(1, 1/12, 1/365))
## [1] 7.1776256 0.4276256 7.4331050

更新:重新排列并添加(1)和(1a)。