Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create new variable based on function of other variables

How can I pass column entires as arguments to a function, then creating a new column which is a function of the other two? For example, taking this excellent function to add months to a date, and taking this example data frame:

df <- structure(
  list(
date = structure(
  c(
    17135,
    17105,
    17105,
    17074,
    17286,
    17317,
    17317,
    17347,
    17105,
    17317
  ),
  class = "Date"
),
monthslater = c(10,
                11, 13, 14, 3, 3, 3, 3, 4, NA)
  ),
  .Names = c("date", "monthslater"),
  row.names = c(NA, 10L),
  class = "data.frame"
)

I would like to create a new column where I pass the entries from columns date and monthslater to the function add.months I would have thought that something like this would work:

df$newdate <- add.months(df$date, df$monthslater)

But it doesn't.

The full code for the function is:

add.months <- function(date,n) seq(date, by = paste(n, "months"), length = 2)[2]
like image 542
steve zissou Avatar asked Jan 28 '23 19:01

steve zissou


2 Answers

Using %m+% from the lubridate-package:

library(lubridate)
df$newdate <- df$date %m+% months(df$monthslater)

gives:

> df
         date monthslater    newdate
1  2016-11-30          10 2017-09-30
2  2016-10-31          11 2017-09-30
3  2016-10-31          13 2017-11-30
4  2016-09-30          14 2017-11-30
5  2017-04-30           3 2017-07-30
6  2017-05-31           3 2017-08-31
7  2017-05-31           3 2017-08-31
8  2017-06-30           3 2017-09-30
9  2016-10-31           4 2017-02-28
10 2017-05-31           4 2017-09-30

In a similar way you can also add days or years:

df$newdate2 <- df$date %m+% days(df$monthslater)
df$newdate3 <- df$date %m+% years(df$monthslater)

which gives:

> df
         date monthslater    newdate   newdate2   newdate3
1  2016-11-30          10 2017-09-30 2016-12-10 2026-11-30
2  2016-10-31          11 2017-09-30 2016-11-11 2027-10-31
3  2016-10-31          13 2017-11-30 2016-11-13 2029-10-31
4  2016-09-30          14 2017-11-30 2016-10-14 2030-09-30
5  2017-04-30           3 2017-07-30 2017-05-03 2020-04-30
6  2017-05-31           3 2017-08-31 2017-06-03 2020-05-31
7  2017-05-31           3 2017-08-31 2017-06-03 2020-05-31
8  2017-06-30           3 2017-09-30 2017-07-03 2020-06-30
9  2016-10-31           4 2017-02-28 2016-11-04 2020-10-31
10 2017-05-31           4 2017-09-30 2017-06-04 2021-05-31
like image 73
Jaap Avatar answered Jan 31 '23 09:01

Jaap


For your immediate, specific issue, consider mapply to pass those two vectors element-wise into defined function. And since monthslater includes NA, add a tryCatch to defined function.

add.months <- function(date, n) {
  tryCatch(seq(date, by = paste(n, "months"), length = 2)[2],
           warning = function(w) return(NA),
           error = function(e) return(NA))
}

df$newdate <- as.Date(mapply(add.months, df$date, df$monthslater), origin="1970-01-01")
df

#          date monthslater    newdate
# 1  2016-11-30          10 2017-09-30
# 2  2016-10-31          11 2017-10-01
# 3  2016-10-31          13 2017-12-01
# 4  2016-09-30          14 2017-11-30
# 5  2017-04-30           3 2017-07-30
# 6  2017-05-31           3 2017-08-31
# 7  2017-05-31           3 2017-08-31
# 8  2017-06-30           3 2017-09-30
# 9  2016-10-31           4 2017-03-03
# 10 2017-05-31          NA       <NA>

Also, do note the author's item involving end of February and hence #9 is extended 3 days ahead.

like image 27
Parfait Avatar answered Jan 31 '23 07:01

Parfait