2013-03-05 5 views
6

Ho un data.table con dati ordinati etichettati e voglio aggiungere una colonna che mi dice quanti record fino a quando non arrivo a un record "speciale" che resetta il conto alla rovescia.Aggiungere una colonna conto alla rovescia alle righe contenenti data.table fino a quando una riga speciale ha incontrato

Ad esempio:

DT = data.table(idx = c(1,3,3,4,6,7,7,8,9), 
       name = c("a", "a", "a", "b", "a", "a", "b", "a", "b")) 
setkey(DT, idx) 
#manually add the answer 
DT[, countdown := c(3,2,1,0,2,1,0,1,0)] 

> DT 
    idx name countdown 
1: 1 a   3 
2: 3 a   2 
3: 3 a   1 
4: 4 b   0 
5: 6 a   2 
6: 7 a   1 
7: 7 b   0 
8: 8 a   1 
9: 9 b   0 

vedere come la colonna di conto alla rovescia mi dice quante righe fino a quando una fila denominata "b". La domanda è come creare quella colonna nel codice.

Si noti che la chiave non è equidistante e può contenere duplicati (quindi non è molto utile per risolvere il problema). In generale i nomi non-b potrebbero essere diversi, ma potrei aggiungere una colonna fittizia che sia solo True/False se la soluzione lo richiede.

risposta

7

Ecco un'altra idea:

## Create groups that end at each occurrence of "b" 
DT[, cd:=0L] 
DT[name=="b", cd:=1L] 
DT[, cd:=rev(cumsum(rev(cd)))] 
## Count down within them 
DT[, cd:=max(.I) - .I, by=cd] 
# idx name cd 
# 1: 1 a 3 
# 2: 3 a 2 
# 3: 3 a 1 
# 4: 4 b 0 
# 5: 6 a 2 
# 6: 7 a 1 
# 7: 7 b 0 
# 8: 8 a 1 
# 9: 9 b 0 
+0

+1. Stavo solo leggendo i documenti su '.I' e cercando di pensare a come potrebbe essere utile qui, ma non ho abbastanza familiarità con' data.table' per capire da dove cominciare! – A5C1D2H2I1M1N2O1R2T1

+0

Sono tutte buone risposte, ma mi piace questo dato che non usa variabili extra per eseguire il lavoro. – Corone

6

Sono sicuro (o almeno spero) che una soluzione puramente "data.table" sarebbe generata, ma nel frattempo, si potrebbe fare uso di rle. In questo caso, sei interessato a invertire il conto alla rovescia, quindi utilizzeremo lo rev per invertire i valori del "nome" prima di procedere.

output <- sequence(rle(rev(DT$name))$lengths) 
makezero <- cumsum(rle(rev(DT$name))$lengths)[c(TRUE, FALSE)] 
output[makezero] <- 0 

DT[, countdown := rev(output)] 
DT 
# idx name countdown 
# 1: 1 a   3 
# 2: 3 a   2 
# 3: 3 a   1 
# 4: 4 b   0 
# 5: 6 a   2 
# 6: 7 a   1 
# 7: 7 b   0 
# 8: 8 a   1 
# 9: 9 b   0 
+0

Anche se arriva una risposta pura 'data.table', questo è un +1 preciso per il pensiero laterale. E, naturalmente, funziona perfettamente. – Corone

+3

@Corone - Una cosa che non hai detto è quello che ti piacerebbe accadere quando (se mai) ci sono due "b" in una riga. Ananda e le mie risposte differiscono in questo caso: il mio assegna uno "0" ad entrambi, mentre inizia il conto alla rovescia per il gruppo successivo che segue la prima "b". –

+0

@ JoshO'Brien buon punto: il risultato corretto è che b dovrebbe sempre mostrare zero. – Corone

3

Ecco un mix di soluzioni di Josh e Ananda di, in quanto, io uso RLE per generare il modo in cui Josh ha dato la risposta:

t <- rle(DT$name) 
t <- t$lengths[t$values == "a"] 
DT[, cd := rep(t, t+1)] 
DT[, cd:=max(.I) - .I, by=cd] 

Ancora meglio: approfittando del fatto che c'è sempre un solo b (o supponiamo che sia qui), si potrebbe fare meglio questo:

t <- rle(DT$name) 
t <- t$lengths[t$values == "a"] 
DT[, cd := rev(sequence(rev(t+1)))-1] 

Edit: Dal commento di OP, sembra chiaro che non v'è più di 1 b possibile e in questi casi, tutto b dovrebbe essere 0. Il primo passo per farlo è quello di creare gruppi dove b finisce dopo ogni consecutivi a.

DT <- data.table(idx=sample(10), name=c("a","a","a","b","b","a","a","b","a","b")) 
t <- rle(DT$name) 
val <- cumsum(t$lengths)[t$values == "b"] 
DT[, grp := rep(seq(val), c(val[1], diff(val)))] 
DT[, val := c(rev(seq_len(sum(name == "a"))), 
     rep(0, sum(name == "b"))), by = grp] 

#  idx name grp val 
# 1: 1 a 1 3 
# 2: 7 a 1 2 
# 3: 9 a 1 1 
# 4: 4 b 1 0 
# 5: 2 b 1 0 
# 6: 8 a 2 2 
# 7: 6 a 2 1 
# 8: 3 b 2 0 
# 9: 10 a 3 1 
# 10: 5 b 3 0 
+0

+1 per un'altra bella alternativa! Basta guardare fuori che qualcuno non viene e criticare la tua scelta di 't' come nome di un oggetto :) – A5C1D2H2I1M1N2O1R2T1

+0

Sfortunatamente la nostra ipotesi riguardo solo 1" b "(lo pensavo anch'io) [non è corretta] (http: // StackOverflow .it/questions/15231433/add-a-countdown-column-to-data-table-contenenti-rows-until-a-special-row-encount/15231752 # comment21474827_15231752) :( – A5C1D2H2I1M1N2O1R2T1

+0

Sì, sto lavorando a una soluzione per tutti i 'b' devono essere sostituiti da 0. – Arun

Problemi correlati