2016-04-28 5 views
5

ich Daten haben, die wie folgt aussieht:Dplyr oder data.table aufeinander folgenden Zeilen innerhalb gruppierten Daten basierend auf dem Wert in einer anderen Spalte konsolidieren

ID CLASS START END 
100 GA 3-Jan-15 1-Feb-15 
100 G 1-Feb-15 22-Feb-15 
100 GA 28-Feb-15 17-Mar-15 
100 G 1-Apr-15 8-Apr-15 
100 G 10-Apr-15 18-Apr-15 
200 FA 3-Jan-14 1-Feb-14 
200 FA 1-Feb-14 22-Feb-14 
200 G 28-Feb-14 15-Mar-14 
200 F 1-Apr-14 20-Apr-14 

Hier die Daten:

df <- structure(list(ID = c(100L, 100L, 100L, 100L, 100L, 200L, 200L, 
200L, 200L), CLASS = structure(c(4L, 3L, 4L, 3L, 3L, 2L, 2L, 
3L, 1L), .Label = c("F", "FA", "G", "GA"), class = "factor"), 
START = structure(c(9L, 4L, 7L, 2L, 5L, 8L, 3L, 6L, 1L), .Label = c("1-Apr-14", 
"1-Apr-15", "1-Feb-14", "1-Feb-15", "10-Apr-15", "28-Feb-14", 
"28-Feb-15", "3-Jan-14", "3-Jan-15"), class = "factor"), 
END = structure(c(2L, 8L, 4L, 9L, 5L, 1L, 7L, 3L, 6L), .Label = c("1-Feb-14", 
"1-Feb-15", "15-Mar-14", "17-Mar-15", "18-Apr-15", "20-Apr-14", 
"22-Feb-14", "22-Feb-15", "8-Apr-15"), class = "factor")), .Names = c("ID", 
"CLASS", "START", "END"), class = "data.frame", row.names = c(NA, 
-9L)) 

Ich möchte um die Daten nach der ID-Spalte zu gruppieren und anschließend alle aufeinander folgenden Vorkommen desselben Werts in der Spalte CLASS zu konsolidieren (sortiert nach dem START-Datum), wobei das minimale Startdatum und das maximale Enddatum ausgewählt werden. Für die ID-Nummer 100 gibt es nur eine Instanz, bei der die Klasse "G" fortlaufend ist. Daher möchte ich diese beiden Zeilen zu einer einzelnen Zeile mit den Daten min (START) und max (END) zusammenfassen. Dies ist ein einfaches Beispiel, aber in den echten Daten gibt es manchmal mehrere aufeinanderfolgende Zeilen, die konsolidiert werden müssen.

Ich habe versucht, group_by gefolgt von einer Art Ranking, aber das scheint nicht den Trick zu tun. Irgendwelche Vorschläge, wie man das löst? Auch dies ist das erste Mal, dass ich auf SO poste, also hoffe ich, dass diese Frage Sinn macht.

Ergebnis soll wie folgt aussehen:

ID CLASS START END 
100 GA 3-Jan-15 1-Feb-15 
100 G 1-Feb-15 22-Feb-15 
100 GA 28-Feb-15 17-Mar-15 
100 G 1-Apr-15 18-Apr-15 
200 FA 3-Jan-14 22-Feb-14 
200 G 28-Feb-14 15-Mar-14 
200 F 1-Apr-14 20-Apr-14 
+1

Sollen die beiden 'FA's in ID 200 nicht wie w konsolidiert werden? Elle? –

+0

@CactusWoman das ist richtig! Ich werde die Tabelle bearbeiten. – Kartik

Antwort

5

Hier ist eine Option, mit data.table::rleid eine ID für die Läufe der gleichen ID und CLASS zu machen:

# make START and END Date class for easier manipulation 
df <- df %>% mutate(START = as.Date(START, '%d-%b-%y'), 
        END = as.Date(END, '%d-%b-%y')) 
# More concise alternative: 
# df <- df %>% mutate_each(funs(as.Date(., '%d-%b-%y')), START, END) 

# group and make rleid as mentioned above 
df %>% group_by(ID, CLASS, rleid = data.table::rleid(ID, CLASS)) %>% 
    # collapse with summarise, replacing START and END with their min and max for each group 
    summarise(START = min(START), END = max(END)) %>% 
    # clean up arrangement and get rid of added rleid column 
    ungroup() %>% arrange(rleid) %>% select(-rleid) 

# Source: local data frame [7 x 4] 
# 
#  ID CLASS  START  END 
# (int) (fctr)  (date)  (date) 
# 1 100  GA 2015-01-03 2015-02-01 
# 2 100  G 2015-02-01 2015-02-22 
# 3 100  GA 2015-02-28 2015-03-17 
# 4 100  G 2015-04-01 2015-04-18 
# 5 200  FA 2014-01-03 2014-02-22 
# 6 200  G 2014-02-28 2014-03-15 
# 7 200  F 2014-04-01 2014-04-20 

Hier ist die reine data.table analog :

library(data.table) 
setDT(df) 
datecols = c("START","END") 
df[, (datecols) := lapply(.SD, as.IDate, format = '%d-%b-%y'), .SDcols = datecols] 

df[, .(START = START[1L], END = END[.N]), by=.(ID, CLASS, r = rleid(ID, CLASS))][, r := NULL][] 
+1

Schön, obwohl ich die schlecht formatierten Datumsspalten überschreiben würde, anstatt nur die Umwandlung in einem Teil einer Kette zu machen. Ich meine 'df = df%>% mutate_each (Spaß (.%>% As.Date (Format = '% d-% b-% y')), START, ENDE)' vor dem anderen Zeug. – Frank

+1

@Frank Ja, ich ging für Lesbarkeit über Prägnanz. Ich denke, das prägnanteste wäre, das mutierte durch mutate_each (Spaß (asDate (.,% D-% b-% y)), START, END) zu ersetzen. (Und natürlich ist die Bearbeitung von data.table willkommen!) – alistaire

+2

@Alistair Schöne 'data.table' Lösung. Aber Achtung, die Datumsumwandlung funktioniert nur für das englische Gebietsschema. Ich musste 'Sys.setlocale (" LC_ALL "," English ") auf meinem System benutzen. Die '% b'-Konvertierungsspezifikation erwartet den Monatsnamen im aktuellen Gebietsschema. – Uwe