【问题标题】:Using ifelse statement in a sapply function在 sapply 函数中使用 ifelse 语句
【发布时间】:2017-04-10 14:47:05
【问题描述】:

我正在尝试在我的 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 格式。

【问题讨论】:

  • 你没有在函数中使用你的 x 参数。
  • 不,您传递给sapply 的函数有一个参数x 没有在函数中使用。相反,你使用df(或者start_date什么都不用)。
  • 实际上,问题是你在seqfunction 中传递了需要数值的日期。
  • 是的,伙计们,@Starwhat 的函数中使用了 x

标签: r if-statement nested apply


【解决方案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 日期操作函数,因为它们通常很糟糕;我建议改用‹lubridate›包。

【讨论】:

  • 尽管 seq.int(r1$mon, 12 * (to0$year - r1$year) + to0$mon, by) 中的错误仍然出现此错误:'by' 参数中的错误登录
  • @Strawhat 啊,很好的一点,它仍然会在负月份失败。嗯。查看我的编辑。
  • 它可以工作,尽管运行 mapply 函数需要很长时间。我会等待其他回复,然后我会投票。
【解决方案2】:

我认为您的方法过于复杂。如果你打算使用sapply,你应该能够避免ifelse,因为你将能够一次专注于一个值(假设你正在通过sapply运行一个向量。这可能不会如果通过sapply 运行列表,则为真)。但是,如果您真的想使用apply 函数,最好使用带有if ... else 子句的mapply

apply 函数根本不需要。事实上,ifelse 函数不是必需的。您可以通过以下方式大大简化流程:

# 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

我所做的只是计算所有变量的差异,获取负值的索引,然后将它们替换为 1。

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

DFrame$diff2 <- vector("numeric", nrow(DFrame))
end_first <- DFrame$end < DFrame$start
DFrame$diff2[!end_first] <- elapsed_months(DFrame$end[!end_first], DFrame$start[!end_first]) 
DFrame$diff2[end_first] <- 1

【讨论】:

  • - 是否会在日期之间进行月份差异?
  • 不,它没有。老实说,在看到您的答案之前,我并没有过多地阅读他的代码。我刚刚看到sapplyifelse 并且知道会有更好的解决方案。他的回答文字只是说日期不同。我会调整我的答案以获得所需的输出。
  • 我从stackoverflow.com/questions/1995933/… 借用了代码,以便在几个月内获得一个不错的基本解决方案。某处可能有一个不错的lubridate 解决方案,但我没有花任何时间寻找它。 @康拉德鲁道夫
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-21
  • 1970-01-01
  • 1970-01-01
  • 2021-01-25
  • 2018-10-25
相关资源
最近更新 更多