2016-08-19 162 views
6

我想合併數據框的行,使「開始」和「結束」列所描述的範圍包含原始數據集的所有值。可能有重疊,重複和嵌套範圍。一些範圍可能會丟失。根據日期範圍合併行

這是我想崩潰的數據類型的例子:

data = data.frame(rbind(
    c("Roger", 1, 10), 
    c("Roger", 10, 15), 
    c("Roger", 16, 17), 
    c("Roger", 3, 6), 
    c("Roger", 20, 25), 
    c("Roger", NA, NA), 
    c("Susan", 2, 8))) 
names(data) = c("name", "start", "end") 
data$start = as.numeric(as.character(data$start)) 
data$end = as.numeric(as.character(data$end)) 

期望的結果將是:

name start end 
Roger 1  17 
Roger 20 25 
Susan 2  8 

我的嘗試是每個項目中展開了範圍爲每行。這有效,但我不知道如何縮小它。另外,我正在使用的完整數據集有大約3000萬行和非常大的範圍,所以這種方法非常慢。

pb <- txtProgressBar(min = 0, max = length(data$name), style = 3) 
mylist = list() 
for(i in 1:length(data$name)){ 
    subdata = data[i,] 
    if(is.na(subdata$start)){ 
    mylist[[i]] = subdata 
    mylist[[i]]$each = NA 
    } 
    if(!is.na(subdata$start)){ 
    sequence = seq(subdata$start, subdata$end) 
    mylist[[i]] = subdata[rep(1, each = length(sequence)),] 
    mylist[[i]]$daily = sequence 
    } 
    setTxtProgressBar(pb, i) 
} 

rbindlist(mylist) 
+0

也許這很明顯,但爲什麼羅傑出現兩次?而不是在start = 1和end = 25的行中。 – snoram

+0

@snoram好問題。因爲羅傑沒有18或19,所以這兩個記錄反映了他的範圍內的差距。 – Nancy

+2

相關:[在R中摺疊相交區域](http://stackoverflow.com/questions/16957293/collapse-intersecting-regions-in-r)和[合併重疊範圍到唯一組中](http://stackoverflow.com/questions/15235821/merge-overlapping-ranges-into-unique-groups) – Henrik

回答

10

我猜IRanges爲這個更有效的,但是......

library(data.table) 

# remove missing values 
DT = na.omit(setDT(data)) 

# sort 
setorder(DT, name, start) 

# mark threshold for a new group 
DT[, high_so_far := shift(cummax(end), fill=end[1L]), by=name] 

# group and summarise 
DT[, .(start[1L], end[.N]), by=.(name, g = cumsum(start > high_so_far + 1L))] 

#  name g V1 V2 
# 1: Roger 0 1 17 
# 2: Roger 1 20 25 
# 3: Susan 1 2 8 

工作原理:

  • cummax是累計最高,所以目前爲止的最高值,包括當前行。
  • 要取出排除當前行的值,請使用shift(從前一行中抽取)。
  • cumsum(some_condition)是製作分組變量的標準方式。
  • .N是由by=確定的組的最後一行。

如果需要,可以在最後一步中命名這些列,如.(s = start[1L], e = end[.N])


隨着日期的時間間隔。如果使用日期,我會建議IDate課程;只需使用as.IDate即可轉換爲Date

我們可以+1約會,但可惜不能cummax,所以...

cummax_idate = function(x) (setattr(cummax(unclass(x)), "class", c("Date", "IDate"))) 

set.seed(1) 
d = sample(as.IDate("2011-11-11") + 1:10) 
cummax_idate(d) 
# [1] "2011-11-14" "2011-11-15" "2011-11-16" "2011-11-18" "2011-11-18" 
# [6] "2011-11-19" "2011-11-20" "2011-11-20" "2011-11-21" "2011-11-21" 

我覺得這個功能可以代替cummax使用。

功能中的額外()是因爲setattr不會打印其輸出。

+0

我會在'setDT'之後移動'na.omit'以最終使用更快的'na.omit.data.table'方法。 – jangorecki

+0

好的,完成了。謝謝,@jangorecki – Frank

+0

@這太好了。我實際上使用這個日期範圍,但將日期轉換爲數字,然後返回日期與此方法一起使用並保留日期。 – Nancy