在sapply函数中使用ifelse语句

时间:2017-04-10 14:47:06

标签: r if-statement nested apply

我正在尝试在data.table中创建一个新列。我有两列,一列有开始日期,另一列有结束日期。开始日期始终是2016-02-28。某些情况下的结束日期为2014-12-31,而其他情况则为2020-12-31(均为YYYY-MM-DD格式)。

在第一种情况下,显然我应该在日期中得到负面差异。在第二种情况下,它是积极的。

我想使用带有ifelse语句的sapply函数来确定日期的差异。任何时候,差异都是负面的,我希望R用值1替换它。

我这样做如下。

sapply(df$end.date, function(x) { ifelse(df$end.date>start_date, as.integer(length(seq(from=start_date, to=as.POSIXct(x,format="%Y-%m-%d"), by ='month')) ), 1) } )

不幸的是,我收到以下错误

Error in seq.POSIXt(from = start_date, to = as.POSIXct(df$end.date,  : 
  'from' must be of length 1

我该如何做到这一点?

PS:start_date和df $ end.date在data.table中都是POSIXct格式。

2 个答案:

答案 0 :(得分:1)

ifelse已经向量化,加倍sapplyifelse是多余的。

不幸的是ifelse在这里不起作用,因为我们无法获得负日期的月份差异(根据您的评论)。因此,我们只需将ifmapply结合使用:

months_between = function (start, end) {
     if (end > start)
         length(seq(start, end, by = 'month'))
     else
         1
}

df$new_column = mapply(months_between, df$start.date, df$end.date)

我也很确定有更好的方法来编写months_between,但我不熟悉基本R日期操作函数,因为它们通常非常糟糕;我建议改用包。

答案 1 :(得分:1)

我认为你的方法过于复杂。如果您要使用sapply,则应该能够避免使用ifelse,因为您可以一次关注一个值(假设您正在运行向量{{1}如果通过sapply运行列表,则可能不成立。但是,如果您确实想使用sapply函数,最好将applymapply子句一起使用。

if ... else功能根本不需要。实际上,apply函数不是必需的。您可以通过以下方式简化流程:

ifelse

我所做的只是计算所有变量的差异,获得负值的索引,并将其替换为1.

另一种方法是预先编制索引。这样,您就不会计算最终更改的任何值的日期差异。如果你有几百万行,这可能会带来好处,但我猜想性能提升会很小。

# Borrowed code from http://stackoverflow.com/questions/1995933/number-of-months-between-two-dates/1996404
elapsed_months <- function(end_date, start_date) {
  mapply(
    function(end_date, start_date){
      ed <- as.POSIXlt(end_date)
      sd <- as.POSIXlt(start_date)
      12 * (ed$year - sd$year) + (ed$mon - sd$mon)
    },
    end_date,
    start_date,
    SIMPLIFY = FALSE
  )
}


DFrame <- data.frame(start = rep(as.Date("2016-02-28"), 2),
                     end = as.Date(c("2014-12-31", "2020-12-31")))

DFrame$diff <- elapsed_months(DFrame$end, DFrame$start)
DFrame$diff[DFrame$diff < 0] <- 1

DFrame