2016-04-28 16 views
5

mam dane, które wygląda następująco:Dplyr lub data.table konsolidacji kolejnych wierszy w obrębie zgrupowanych danych na podstawie wartości w innej kolumnie

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 

Oto dane:

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

chciałbym grupować dane według kolumny ID, a następnie konsolidować kolejne wystąpienia tej samej wartości w kolumnie KLASA (posortowane według daty START), wybierając minimalną datę rozpoczęcia i maksymalną datę zakończenia. Tak więc dla numeru identyfikacyjnego 100 istnieje tylko jeden przypadek, w którym klasa "G" jest kolejna, dlatego chciałbym skonsolidować te dwa wiersze w jednym wierszu z datami min (START) i max (END). Jest to prosty przykład, ale w rzeczywistych danych czasami istnieje kilka kolejnych wierszy, które muszą zostać skonsolidowane.

Próbowałem group_by następnie za pomocą jakiegoś rankingu, ale to nie wydaje się rade. Wszelkie sugestie, jak rozwiązać ten problem? Również po raz pierwszy zamieszczam na SO, więc mam nadzieję, że to pytanie ma sens.

Wynik powinien wyglądać następująco:

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

Nie należy scalać dwóch "FA" w identyfikatorze 200 jako w łokieć? –

+0

@CactusWoman to prawda! Będę edytować tabelę. – Kartik

Odpowiedz

5

Oto opcja, za pomocą data.table::rleid aby identyfikator dla przebiegów o tej samej ID i CLASS:

# 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 

Oto czysta data.table analogowe :

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

Przyjemnie, chociaż nadpisałbym źle sformatowane kolumny daty, zamiast tylko wykonywać konwersję w części łańcucha. Mam na myśli 'df = df%>% mutate_each (funs (.%>% As.Date (format = '% d-% b-% y')), START, END)' przed innymi rzeczami. – Frank

+1

@Frank Tak, zdecydowałem się na czytelność. Myślę, że najbardziej zwięzła byłaby zamiana 'mutate' na' mutate_each (funs (as.Date (., '% D-% b-% y')), START, END) '. (Oczywiście edytowanie danych jest oczywiście mile widziane!) – alistaire

+2

@Alistair Ładne rozwiązanie 'data.table'. Ale uważaj, konwersja daty działa tylko w języku angielskim. Musiałem użyć 'Sys.setlocale (" LC_ALL "," English ") w moim systemie. Specyfikacja konwersji "% b" oczekuje nazwy miesiąca w bieżącym locale. – Uwe