在大数据框架上优化映射函数

Optimising mapply'ied function over large data frame

我为工作目的创建了以下函数:

monthsCounter <- function(date1, date2) {
  if (date2 < date1) {
    warning("Can't calculate result if second date is older than first date")
  } else {
    date1_Y <- as.numeric(format(date1, '%Y'))
    date2_Y <- as.numeric(format(date2, '%Y'))
    date1_M <- as.numeric(format(date1, '%m'))
    date2_M <- as.numeric(format(date2, '%m'))
    if (date2_Y == date1_Y) {
      date2_M - date1_M
    } else if (date2_M < date1_M) {
      max(0, date2_Y - date1_Y - 1)*12 + 12 - date1_M + date2_M
    } else {
      max(0, date2_Y - date1_Y)*12 + date2_M - date1_M
    }
  }
}

简而言之,它计算两个日期之间的月份,而不管月份日期。 当我 mapply 它在我的数据框上时:

allData$monthsSinceIssue <- mapply(monthsCounter, allData$start_month, allData$Date)

计算时间很长

问题:关于如何优化我的函数以使其计算速度更快,您有什么建议吗?


更新: 根据@Sotos 和@MrGumble 的建议,我最终得到了这个函数:

monthsCounter <- function(date1, date2) {
  date1_Y <- as.numeric(format(date1, '%Y'))
  date2_Y <- as.numeric(format(date2, '%Y'))
  date1_M <- as.numeric(format(date1, '%m'))
  date2_M <- as.numeric(format(date2, '%m'))
  ifelse(date2 < date1, NA,
         ifelse(date2_Y == date1_Y, date2_M - date1_M,
                ifelse(date2_M < date1_M, max(0, date2_Y - date1_Y - 1)*12 + 12 - date1_M + date2_M, max(0, date2_Y - date1_Y)*12 + date2_M - date1_M)))
}

这将我的计算时间从 3.5 分钟减少到 2 秒!

UPDATE2: 我偶然发现了@MrGumble 可能指出的问题。 date2 - date1 > 1 时的情况。 因此必须将功能更新为:

monthsCounter <- function(date1, date2) {
  date1_Y <- as.numeric(format(date1, '%Y'))
  date2_Y <- as.numeric(format(date2, '%Y'))
  date1_M <- as.numeric(format(date1, '%m'))
  date2_M <- as.numeric(format(date2, '%m'))
  ifelse(date2 < date1, NA,
         ifelse(date2_Y == date1_Y, date2_M - date1_M,
                ifelse(date2_M < date1_M, pmax(0, date2_Y - date1_Y - 1)*12 + 12 - date1_M + date2_M, pmax(0, date2_Y - date1_Y)*12 + date2_M - date1_M)))
}

基本上把max改成了pmax

R 天生就可以很好地处理向量。您的函数可以轻松地接受两列作为参数:

allData$monthsSinceIssue <- monthsCounter(allData$start_month, allData$Date)

尽管您必须将 max 更改为 pmax。也按照 Sotos 的建议进行(更新到 ifelse 函数)。 最后我建议你 return NA 而不是警告。