2015-10-05 19 views
10

sono questi miei dati di esempio:somma semplice se l'espressione

dt <- data.table(id=c("a","a","a","a","b","b"), monthsinarrears=c(0,1,0,0,1,0), date=c(2013,2014,2015,2016,2014,2015)) 

La tabella si presenta così:

> dt 
    id monthsinarrears date 
1: a    0 2013 
2: a    1 2014 
3: a    0 2015 
4: a    0 2016 
5: b    1 2014 
6: b    0 2015 

Ora voglio creare una colonna aggiuntiva denominata "EverinArrears" che sarà assegnato con "1" se l'ID era sempre in arretrato (storicamente) e "0" se non lo era. Così l'uscita che voglio ottenere è:

id monthsinarrears date EverinArrears 
1: a    0 2013    0 
2: a    1 2014    1 
3: a    0 2015    1 
4: a    0 2016    1 
5: b    1 2014    1 
6: b    0 2015    1 

Nota che il prestito id a non era storicamente in ritardo nel 2013 (questo è accaduto nel 2014) in modo che è il motivo per cui EverinArrears ottiene un 0 allo zero e nel 2013.

+2

il tuo risultato non corrisponde ai dati che hai dato! –

+0

perché? Voglio semplicemente aggiungere questa colonna aggiuntiva EverinArrears basata sul primo tavolo che ho fornito e sulle condizioni che ho fornito. –

+0

guarda il tuo dt iniziale, e quello che hai scritto ... questo è completamente diverso, anche se la tua domanda è comprensibile in generale;) –

risposta

14

è possibile effettuare le seguenti (grazie a @Roland per il suggerimento per evitare i numeri> 1):

dt[, EverinArrears := as.integer(as.logical(cumsum(monthsinarrears))), by=id] 

uscita:

# id monthsinarrears date EA 
#1: a    0 2013 0 
#2: a    1 2014 1 
#3: a    0 2015 1 
#4: a    0 2016 1 
#5: b    1 2014 1 
#6: b    0 2015 1 

Nota: Se si preferisce un codice più corto , puoi anche fare

dt[, EverinArrears := +(!!(cumsum(monthsinarrears))), by=id] 

sebbene non sia una "buona pratica" come as.integer(as.logical(...))

Come già detto da @Jaap, si può anche fare:

dt[, EverinArrears := +(cumsum(monthsinarrears) > 0), by = id] 

o, per una migliore pratica:

dt[, EverinArrears := as.integer(cumsum(monthsinarrears) > 0), by = id] 

Come suggerito dal @Arun nel commento , un altro, più semplice, modo:

dt[, EverinArrears := cummax(monthsinarrears), by = id] 
+0

questo non funziona è ci sono più di 1 in mesi inarrivati ​​per id ... –

+0

@ColonelBeauvel a destra, buona cattura ... – Cath

+2

Basta inserire in 'as.logical'. – Roland

3

È possibile utilizzare ave:

dt$EverinArrears = as.integer(!!ave(dt$monthsinarrears, dt$id, FUN=cumsum)) 

O il buon approccio con data.table:

dt[, EverinArrears := +(!!cumsum(monthsinarrears)), id][] 
+2

Non utilizzare 'ave' con data.table. Questo è inutilmente lento. – Roland

+0

Ho postato la soluzione 'ave' dato che' !! cumsum() + 0L' non ha funzionato con data.table prima :) –

+1

Usare 'as.logical' e' as.integer' è una pratica migliore (codice più chiaro e marginalmente Più veloce). – Roland

2

Utilizzando pacchetto dplyr:

library(dplyr) 

dt %>% 
    group_by(id) %>% 
    arrange(date) %>% 
    mutate(EverinArrears = +as.logical(cumsum(monthsinarrears))) %>% 
    data.table 

    id monthsinarrears date EverinArrears 
1: a    0 2013    0 
2: a    1 2014    1 
3: a    0 2015    1 
4: a    0 2016    1 
5: b    1 2014    1 
6: b    0 2015    1 
5

Ecco una leggera variazione sulle risposte degli altri:

dt[, newcol := cummax(monthsinarrears > 0), by=id] 

Utilizzando cummax invece di cumsum, potremmo risparmiare su alcuni calcoli.


Ed ecco un modo di confrontare contro la posizione della prima voce con mesi positivi in ​​via posticipata:

dt[, newcol := { 
    z = which(monthsinarrears > 0) 
    if (!length(z)) rep(0L,.N) 
    else   replace(rep(1L,.N), 1:.N < z[1], 0L) 
}, by=id] 

Non sono sicuro se questo potrebbe essere più efficiente; dipende certamente dai dati in una certa misura.