2015-10-05 46 views
1

聯盟:計算我想獲得很多(超過2)區間的聯合衆多間隔

df <- data.frame(id=c(1, 2, 3), 
      interval=c(
       new_interval(ymd("2001-01-01"), ymd("2002-01-01")), 
       new_interval(ymd("2001-01-01"), ymd("2004-01-01")), 
       new_interval(ymd("2001-02-01"), ymd("2002-01-01")) 
       )) 
df 
# id      interval 
# 1 1 2001-01-01 UTC--2002-01-01 UTC 
# 2 2 2001-01-01 UTC--2004-01-01 UTC 
# 3 3 2001-02-01 UTC--2002-01-01 UTC 

lubridate::union(lubridate::union(df$interval[1], df$interval[2]), 
       df$interval[3]) 
# [1] 2001-01-01 UTC--2004-01-01 UTC 

這是正確的結果。

但是爲什麼lubridate::union不適用於Reduce

Reduce(lubridate::union, df$interval) 
# [1] 31536000 94608000 28857600 

間隔對象似乎被轉換爲數字太兒子(在應用union之前)。

相關的https://stackoverflow.com/questions/32909358/maintain-attributes-of-objects-of-class-lubridate-interval

+0

這將是巨大的,任何'lubridate'包的mantainers的可以提高它使用'Reduce'功能允許。我註冊了一個新問題:https://github.com/hadley/lubridate/issues/348 – user3808394

+0

僅供將來參考。如果您在問題仍未解決的情況下打開github問題,請在此問題中記下它,以便人們很容易意識到這一點。我回答了這個問題,但沒有看到你的github問題的鏈接,這個問題在我提交答案之前已經關閉了。乾杯。 –

回答

4

爲什麼這不工作的原因是不Reduce()。相反,它是as.list(),當提供的x參數不是以列表開始時,它應用於x內部的Reduce()。相關的行是Reduce()中的第8行和第9行,如下所示。

head(Reduce, 9) 
# ...               
# 8  if (!is.vector(x) || is.object(x))     
# 9   x <- as.list(x)         

if()條件的快速檢查證實了這一點。

!is.vector(df$interval) || is.object(df$interval) 
# [1] TRUE 

因此as.list()在您的來電Reduce(),這意味着df$intervaldf$interval使用變得

as.list(df$interval) 
# [[1]] 
# [1] 31536000 
# 
# [[2]] 
# [1] 94608000 
# 
# [[3]] 
# [1] 28857600 

Reduce()任何重要的操作發生前(其實這是對我們而言最重要的操作)。這使得Reduce()輸出合理;它會返回所有三個,因爲它們是唯一的。

如果你真的需要使用Reduce()可以繞過列表檢查,首先構建自己的列表,使用for()環路(如lapply()也將無法正常工作)。然後我們可以將其提供給Reduce()並獲得適當的期望輸出。

x <- vector("list", length(df$interval)) 
for(i in seq_along(x)) x[[i]] <- df$interval[i] 

Reduce(lubridate::union, x) 
# [1] 2001-01-01 UTC--2004-01-01 UTC 

但它很可能是最好寫的間隔類的as.list()方法,並把它在你的腳本的頂部。我們可以使用與上面相同的代碼。

as.list.Interval <- function(x, ...) { 
    out <- vector("list", length(x)) 
    for(i in seq_along(x)) out[[i]] <- x[i] 
    out 
} 

Reduce(lubridate::union, df$interval) 
# [1] 2001-01-01 UTC--2004-01-01 UTC 

另外請注意,您可以在此做的另一種方式,通過抓住起始插槽和使用int_end()

interval(min(slot(df$interval, "start")), max(int_end(df$interval))) 
# [1] 2001-01-01 UTC--2004-01-01 UTC 
+0

非常感謝@ richard-scriven。 – user3808394

0

我不知道的情況下Reduce,但我會做這種方式:

library(dplyr) 
library(stringr) 

df %>% 
    mutate(interval = str_trim(str_replace_all(interval, "(--|UTC)", " ")), 
     int_start = word(interval), 
     int_end = word(interval, -1)) %>% 
    summarise(interval = str_c(min(int_start), 
          max(int_end), 
          sep = "--")) 
# result 
       interval 
1 2001-01-01--2004-01-01 
+2

因此,你有一個7行代碼,它提供了與單行'lubridate :: union'相同的結果? – 2015-10-05 10:05:34

+2

@帕斯卡你不必喜歡我的答案。 –

+0

無論如何它不回答這個問題。 – 2015-10-05 11:44:33