2012-10-25 23 views
7

Moje pytanie brzmi: Mam ramkę danych z niektórymi zmiennymi czynnikowymi. Teraz chcę przypisać nowy wektor do tej ramki danych, która tworzy indeks dla każdego podzbioru tych zmiennych czynnikowych.Jak przypisać licznik do określonego podzbioru data.frame, który jest zdefiniowany przez kombinację czynników?

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

Daje mi coś takiego:

 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 

i co chcę to kombinacja licznik który liczy występowania każdej kombinacji czynników. Podoba Ci się to

 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 

Dotychczas myślałem o użyciu Tapply dostać licznik nad wszystkimi czynnik kombinacjach, które działa prawidłowo

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

Ale nie wiem, jak mogę przypisać listę licznika (np niepubliczny) do kombinacji w ramce danych bez użycia nieefektywnej pętli :)

+0

Czy trzeba być w porządku czy po prostu chcą liczy netto? Jeśli chcesz tylko zliczać, może pomóc tabela (wklej (dane $ fac1, dane $ fac2, sep = "-")). – screechOwl

+0

Cześć! W każdej kombinacji fac1 x fac2 kolejność ma znaczenie. (Można pomyśleć o tym, jak razy osoba "fac1" zobaczyła literę "fac2"). – JBJ

+0

Możesz użyć tej samej podstawowej strategii, ale przełącz się z "tapply" na "ddply" z ** plyr **, lub jeśli dane są ogromne, a wydajność to problem, "data.table". – joran

Odpowiedz

6

To jest zadanie dla funkcji 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 

Uwaga wykorzystanie stringsAsFactors=FALSE w kroku data.frame(). Jeśli tego nie zrobiłeś, nadal możesz uzyskać dane wyjściowe za pomocą: myDF$counter <- ave(as.character(myDF$fac2), myDF$fac1, myDF$fac2, FUN = seq_along).

+0

To na pewno jest, + 1 –

+0

Świetna odpowiedź !!!! +1 –

+0

W porównaniu do mrdwab i mojego rozwiązania pod względem wydajności (nie mogłem zmusić @mplourde do pracy) i mrdwab jest dwa razy szybszy. Dla 1000000 linii to 1,693 vs. 3,382 sek. – vaettchen

0

To jest podstawowa metoda R, która unika (jednoznacznego) zapętlenia.

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

Tutaj wariant z trochę pętli (od „danych” Mam przemianowany swoją zmienną „x” jest używane w inny sposób):

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] 

Nie mam pojęcia czy jest skuteczny na dużej data.frame ale działa!

4

data.table rozwiązanie

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