2017-04-10 223 views
0

我想在data.table中创建一个新列。我有两列,一列有开始日期,另一列有结束日期。开始日期总是2016-02-28。有些情况下的结束日期为2014-12-31,其他日期为2020-12-31(均为YYYY-MM-DD格式)。在sapply函数中使用ifelse语句

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

我想用sapply函数和ifelse语句来确定日期的差异。任何时候,差异都是负数,我希望R用值01​​代替它。

我这样做如下。

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:在data.table中,start_date和df $ end.date都是POSIXct格式。

+0

您不在函数中使用'x'参数。 –

+0

不,你传递给'sapply'的函数有一个参数'x',它在函数中没有使用。相反,你可以使用'df'(或者''start_date'')。 –

+0

其实,问题在于你在需要数值的'seq'函数中传递日期。 – Smich7

回答

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日期操作功能精通坏;我建议使用 包代替。

+0

仍然得到这个错误,虽然 seq.int错误(r1 $ mon,12 *(to0 $ year - r1 $ year)+ to0 $ mon,by) : 错误登录'by'参数 – Strawhat

+0

@Strawhat啊,非常好的一点,它仍然会在负数月份失败。 GUH。看我的编辑。 –

+0

它的工作原理虽然需要很多时间来运行mapply函数。我会等待其他答复,然后我会加快。 – Strawhat

1

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

但是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 
+0

'''做日期之间的月份差异吗? –

+0

不,它没有。我会说实话,我没有看到他的代码太多,直到我看到你的答案。我刚看到'sapply'和'ifelse',并且知道会有更好的解决方案。他的回答正文说日期不同。我会调整我的答案以获得所需的输出。 – Benjamin

+0

我借用了http://stackoverflow.com/questions/1995933/number-of-months-between-two-dates/1996404中的代码,在一个体面的基础解决方案中获得了几个月的差异。某处可能有一个不错的'lubridate'解决方案,但我没有花时间去寻找它。 @KonradRudolph – Benjamin