2016-08-19 2 views
6

Ich möchte Zeilen eines Datenrahmens so kombinieren, dass die von einer Spalte "Anfang" und "Ende" beschriebenen Bereiche alle Werte aus dem ursprünglichen Datensatz enthalten. Es kann Überlappungen, Wiederholungen und verschachtelte Bereiche geben. Einige Bereiche fehlen möglicherweise.Konsolidieren Sie Zeilen basierend auf Datumsbereichen

Hier ist ein Beispiel für die Art von Daten, die ich zusammenbrechen mag:

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)) 

Das gewünschte Ergebnis wäre:

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

Mein Versuch, in der jedes Element zu erweitern heraus hat Bereich für jede Zeile. Das funktioniert, aber ich bin mir nicht sicher, wie ich es zurückschrauben soll. Darüber hinaus hat der gesamte Datensatz, mit dem ich arbeite, ungefähr 30 Millionen Zeilen und sehr große Bereiche, daher ist diese Methode SEHR langsam.

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

Vielleicht ist es offensichtlich, aber warum erscheint Roger zweimal? und nicht in einer Reihe mit Start = 1 und Ende = 25? – snoram

+0

@snoram Gute Frage. Da Roger keine 18 oder 19 hatte, spiegeln die beiden Aufzeichnungen die Lücke in seinen Reihen wider. – Nancy

+2

Related: [Überschneidung Regionen in R] (http://stackoverflow.com/questions/16957293/collapse-intersecting-regions-in-r) und [Merge Overlapping Ranges in eindeutige Gruppen] (http://stackoverflow.com/questions/15235821/merge-overlaping-ranges-in-unique-groups) – Henrik

Antwort

10

Ich vermute IRanges ist viel effizienter für diese, aber ...

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 

Wie es funktioniert:

  • cummax die kumulative Maximum ist, so dass die höchster Wert bisher, einschließlich der aktuellen Zeile.
  • Um den Wert ohne die aktuelle Zeile zu verwenden, verwenden Sie shift (die aus der vorherigen Zeile stammt).
  • cumsum(some_condition) ist eine Standardmethode zum Erstellen einer Gruppierungsvariablen.
  • .N ist die letzte Zeile der Gruppe bestimmt durch by=.

Die Spalten können im letzten Schritt wie .(s = start[1L], e = end[.N]) benannt werden, falls gewünscht.


Mit Datum Abständen. Wenn ich mit Daten arbeite, würde ich die Klasse IDate vorschlagen; Verwenden Sie einfach as.IDate, um eine Date zu konvertieren.

Wir können +1 auf Daten, aber leider kann nicht cummax, so ...

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" 

Ich denke, diese Funktion kann anstelle von cummax verwendet werden.

Die extra () in der Funktion sind da setattr wird nicht seine Ausgabe drucken.

+0

Ich würde 'na.omit' verschieben, um nach' setDT' angewendet zu werden, um schließlich 'na.omit.data.table' Methode schneller zu verwenden. – jangorecki

+0

Ok, fertig.Danke, @jangorecki – Frank

+0

@Dies ist großartig. Ich verwende dies tatsächlich für Datumsbereiche, aber das Konvertieren der Datumsangaben in numerische und dann zurück in Datumsangaben funktioniert mit dieser Methode und behält die Daten bei. – Nancy

Verwandte Themen