2016-04-28 11 views
5

Ho dati che assomiglia a questo:Dplyr o data.table consolidare righe consecutive raggio dati raggruppati in base al valore in un'altra colonna

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 

Ecco i dati:

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

desidero per raggruppare i dati per la colonna ID e quindi consolidare eventuali occorrenze consecutive dello stesso valore nella colonna CLASS (ordinate per data START), selezionando la data di inizio minima e la data di fine massima. Quindi, per il numero ID 100, c'è solo un'istanza in cui la classe "G" è consecutiva, quindi vorrei consolidare quelle due righe in una singola riga con le date min (START) e max (END). Questo è un semplice esempio, ma nei dati reali a volte ci sono diverse righe consecutive che devono essere consolidate.

Ho provato group_by seguito utilizzando una sorta di classifica, ma questo non sembra fare il trucco. Qualche suggerimento su come risolvere questo? Anche questa è la prima volta che pubblico su SO, quindi spero che questa domanda abbia senso.

risultato dovrebbe assomigliare a questo:

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

I due 'FA' in ID 200 non devono essere consolidati come w ell? –

+0

@CactusWoman questo è giusto! Modificherò la tabella. – Kartik

risposta

5

Ecco un'opzione, utilizzando data.table::rleid per fare un id per i funzionamenti dello stesso ID e 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 

Ecco l'analogo data.table puro :

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

Bello, anche se sovrascrivo le colonne di data formattate male invece di fare solo la conversione in una parte della catena. Voglio dire 'df = df%>% mutate_each (funs (.%>% As.Date (formato = '% d-% b-% y')), START, END)' prima delle altre cose. – Frank

+1

@Frank Sì, sono andato per la leggibilità sulla concisione. Penso che il più conciso sarebbe quello di sostituire il 'mutate' con' mutate_each (funs (as.Date (., '% D-% b-% y')), START, END) '. (E la modifica data.table è benvenuta, ovviamente!) – alistaire

+2

@Alistair Bella soluzione 'data.table'. Ma attenzione, la conversione della data funziona solo per le impostazioni locali in inglese. Ho dovuto usare 'Sys.setlocale (" LC_ALL "," English ")' sul mio sistema. La specifica della conversione '% b' prevede il nome del mese nelle impostazioni locali correnti. – Uwe

Problemi correlati