Suppose I have a string
age<-c("7y2m4d","5m4d","7y5m6d")
I want to convert it to a numeric vector like
c(7.34, 0.43, 7.43)
How can I make the R code?
We can assume there is 365 days in a year and 365/12 days in a month.
Suppose I have a string
age<-c("7y2m4d","5m4d","7y5m6d")
I want to convert it to a numeric vector like
c(7.34, 0.43, 7.43)
How can I make the R code?
We can assume there is 365 days in a year and 365/12 days in a month.
lubridate::duration will convert your strings to (approximate) seconds.
library(lubridate)
library(magrittr)
age <- c("7y2m4d", "5m4d", "7y5m6d")
age_sec <- age %>%
duration() %>%
as.numeric()
age_sec
[1] 226508400 13494600 234570600
Then you can approximate years as 365 * 24 * 60 * 60 seconds:
age_sec / (365 * 24 * 60 * 60)
[1] 7.182534 0.427911 7.438185
Another solution with base 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
The idea is to create the formula and then evaluate it for each element of your vector.
These solutions:
age for which the question appears to have computed the answer incorrectly)Comparing the solutions below on the basis of simplicity (1a) is the simplest and automatically handles all the edge cases without specific code for them suggesting that it is the most natural; however, it does make use of a package. (1) is only slightly more complex and uses no packages and (2) pretty short and also does not use any packages but it is not as simple as (1) or (1a).
1) Here getNum extracts and returns the number from x associated with the code (the code is "y", "m" or "d") or if the code is not present in x returns 0. We then add up the year, month/12 and day/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) This is similar to (1) except that we use strapply in gsubfn to simplify getNum. In fact getNum reduces to a single strapply call and the regular expression it uses is also simpler.
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) This alternative converts each string to dcf format and uses read.dcf to create a matrix of the y, m and d numbers.
In detail, the first line of code is to handle certain edge cases which are not actually present in the sample data in the question. We first append 0d to age (from the question) if d is missing so that we can handle the case where y, m and d are all missing. We also prepend a dummy entry to ensure that y, m and d are present in at least one entry. If we knew that y, m and d were present in at least one component and there was no component in which y, m and d were all simultaneously missing then this first line of code could be omitted.
The second line of code converts each input character string to dcf form and reads it into a matrix ensuring that the columns are in a known order and deleting the dummy entry added above.
Finally we replace NAs with 0 and and use matrix multiplication to add up the year, month/12 and day/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
Update: Rearranged and added (1) and (1a).