2012-11-27 16 views
5

Ho una serie di dati che assomiglia a questo:Calcolare le differenze tra le righe più velocemente di un ciclo for?

ID | DATE | SCORE 
------------------------- 
123 | 1/15/10 | 10 
123 | 1/1/10 | 15 
124 | 3/5/10 | 20 
124 | 1/5/10 | 30 
... 

Quindi, per caricare il frammento di cui sopra come un frame di dati, il codice è:

id<-c(123,123,124,124) 
date<-as.Date(c('2010-01-15','2010-01-01','2010-03-05','2010-01-05')) 
score<-c(10,15,20,30) 
data<-data.frame(id,date,score) 


Sto cercando di aggiungere un colonna che calcola i "giorni dall'ultimo record per questo ID".

In questo momento sto utilizzando un ciclo FOR che sembra qualcosa di simile:

data$dayssincelast <- rep(NA, nrow(data)) 
for(i in 2:nrow(data)) { 
    if(data$id[i] == data$id[i-1]) 
    data$dayssincelast[i] <- data$date[i] - data$date[i-1] 
} 


Esiste un modo più veloce per fare questo? (Ho cercato un po 'in APPLY ma non riesco a capire una soluzione oltre a un ciclo FOR.)

Grazie in anticipo!

+2

Si prega di aggiungere alla domanda l'output di 'dput (testa (dati))'. Le tue date non assomigliano a qualcosa che puoi sottrarre – GSee

+1

Ci sono molti modi per approcciare il pezzo che applica la divisione, ma tutti probabilmente finiranno per usare 'diff'. – joran

+0

@GSee - Non l'ho mostrato, ma ho convertito le date già usando as.Date(). Quanto sopra è solo un dato fittizio per illustrare la struttura. –

risposta

5

Questo dovrebbe funzionare se le date sono in ordine entro id.

id<-c(123,123,124,124) 
date<-as.Date(c('2010-01-15','2010-01-01','2010-03-05','2010-01-05')) 
score<-c(10,15,20,30) 
data<-data.frame(id,date,score) 

data <- data[order(data$id,data$date),] 
data$dayssincelast<-do.call(c,by(data$date,data$id,function(x) c(NA,diff(x)))) 
# Or, even more concisely 
data$dayssincelast<-unlist(by(data$date,data$id,function(x) c(NA,diff(x)))) 
+0

(La mia modifica ha aggiunto la riga di ordinazione) –

+0

(Nessuna modifica. –

0

Come funziona il seguente lavoro?

indx <- which(data$id == c(data$id[-1], NA)) 
data$date[indx] - data$date[indx+1] 



Questo sposta solo i id 's da 1 e li confronta con id per verificare la presenza di partite vicine.
Quindi per la sottrazione dei dati, è sufficiente sottrarre le corrispondenze dalla data della riga successiva.

0

Nel caso in cui avete bisogno di una formula più complessa, è possibile utilizzare aggregato:

a <- aggregate(date ~ id, data=data, FUN=function(x) c(NA,diff(x))) 
data$dayssincelast <- c(t(a[-1]), recursive=TRUE) # Remove 'id' column 

Lo stesso ordinamento vale qui come in @nograpes risposta.

Problemi correlati