2012-10-25 13 views
7

La mia domanda è: ho un frame di dati con alcune variabili fattore. Ora voglio assegnare un nuovo vettore a questo frame di dati, che crea un indice per ogni sottoinsieme di tali variabili fattoriali.Come assegnare un contatore a un sottoinsieme specifico di un data.frame definito da una combinazione di fattori?

data <-data.frame(fac1=factor(rep(1:2,5)), fac2=sample(letters[1:3],10,rep=T)) 

mi dà qualcosa di simile:

 fac1 fac2 
    1  1 a 
    2  2 c 
    3  1 b 
    4  2 a 
    5  1 c 
    6  2 b 
    7  1 a 
    8  2 a 
    9  1 b 
    10 2 c 

E quello che voglio è un contatore combinazione che conta il verificarsi di ogni combinazione fattore. Ti piace questa

 fac1 fac2 counter 
    1  1 a  1 
    2  2 c  1 
    3  1 b  1 
    4  2 a  1 
    5  1 c  1 
    6  2 b  1 
    7  1 a  2 
    8  2 a  2 
    9  1 b  2 
    10 1 a  3 

Finora ho pensato di usare Tapply per ottenere il contatore su tutti Factor-combinazioni, che funziona bene

counter <-tapply(data$fac1, list(data$fac1,data$fac2), function(x) 1:length(x)) 

Ma io non so come posso assegnare l'elenco dei contatori (ad esempio, non in elenco) alle combinazioni nel frame di dati senza utilizzare looping inefficiente :)

+0

Ha bisogno di essere in ordine o vuoi semplicemente conta netti? Se vuoi solo conteggi, la tabella (incolla (dati $ fac1, data $ fac2, sep = "-")) potrebbe aiutare. – screechOwl

+0

Ciao! All'interno di ciascuna combinazione fac1 x fac2 l'ordine conta. (Si può pensare a volte che una persona "fac1" ha visto la lettera "fac2") – JBJ

+0

Puoi usare la stessa strategia di base, ma passare da 'tapply' a' ddply' da ** plyr **, o se il tuo i dati sono enormi e le prestazioni sono un problema, 'data.table'. – joran

risposta

6

Questo è un lavoro per la funzione ave():

# Use set.seed for reproducible examples 
# when random number generation is involved 
set.seed(1) 
myDF <- data.frame(fac1 = factor(rep(1:2, 7)), 
        fac2 = sample(letters[1:3], 14, replace = TRUE), 
        stringsAsFactors=FALSE) 
myDF$counter <- ave(myDF$fac2, myDF$fac1, myDF$fac2, FUN = seq_along) 
myDF 
# fac1 fac2 counter 
# 1  1 a  1 
# 2  2 b  1 
# 3  1 b  1 
# 4  2 c  1 
# 5  1 a  2 
# 6  2 c  2 
# 7  1 c  1 
# 8  2 b  2 
# 9  1 b  2 
# 10 2 a  1 
# 11 1 a  3 
# 12 2 a  2 
# 13 1 c  2 
# 14 2 b  3 

Nota l'uso della stringsAsFactors=FALSE nella fase data.frame(). Se non lo hai, puoi comunque ottenere l'output con: myDF$counter <- ave(as.character(myDF$fac2), myDF$fac1, myDF$fac2, FUN = seq_along).

+0

Certamente è, + 1 –

+0

Ottima risposta !!!! +1 –

+0

Rispetto mrdwab e la mia soluzione in termini di efficienza (non è possibile ottenere @mplourde per funzionare) e il mrdwab è due volte più veloce. Per 1000000 linee è 1.693 contro 3.382 sec – vaettchen

0

Questo è un modo di base R che evita il loop (esplicito).

data$counter <- with(data, { 
    inter <- as.character(interaction(fac1, fac2)) 
    names(inter) <- seq_along(inter) 
    inter.ordered <- inter[order(inter)] 
    counter <- with(rle(inter.ordered), unlist(sapply(lengths, sequence))) 
    counter[match(names(inter), names(inter.ordered))] 
}) 
0

Ecco una variante con un po 'di loop (io ho rinominato la variabile a "x", in quanto "i dati" viene utilizzato in altro modo):

x <-data.frame(fac1=rep(1:2,5), fac2=sample(letters[1:3],10,rep=T)) 
x$fac3 <- paste(x$fac1, x$fac2, sep="") 
x$ctr <- 1 
y <- table(x$fac3) 
for(i in 1 : length(rownames(y))) 
    x$ctr[x$fac3 == rownames(y)[i]] <- 1:length(x$ctr[x$fac3 == rownames(y)[i]]) 
x <- x[-3] 

idea se questo è efficace su una vasta data.frame ma funziona!

4

Una soluzione data.table

library(data.table) 
DT <- data.table(data) 
DT[, counter := seq_len(.N), by = list(fac1, fac2)]