2017-04-10 204 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