2012-08-08 10 views
8

Grupları orijinal sırada tutarken bir veri çerçevesini toplamakta sorun yaşıyorum (veri çerçevesindeki ilk görünüme göre sırayla). Onu doğru şekilde başarmayı başardım, ama bunun için daha kolay bir yol olduğunu umuyordum.Orijinal siparişi koruyarak toplu veri çerçevesi, basit bir şekilde

set.seed(7) 
sel.1 <- sample(1:5, 20, replace = TRUE)  # selection vector 1 
sel.2 <- sample(1:5, 20, replace = TRUE) 
add.1 <- sample(81:100)      # additional vector 1 
add.2 <- sample(81:100) 
orig.df <- data.frame(sel.1, sel.2, add.1, add.2) 

Bazı noktalara Not: Burada

üzerinde çalışmak için ayarlanmış örnek bir veri olduğunu veri gruplanmış nasıl belirlemek için iki seçim sütun yer almaktadır. Aynı olacaklar ve isimleri biliniyor. Bu verilere sadece iki tane ek sütun koydum, ama daha fazlası olabilir. Takip etmeyi kolaylaştırmak için 'sel' ve 'add' ile başlayan sütun adlarını verdim, ancak asıl veriler farklı isimlere sahip (bu yüzden grep püf noktaları güzelken, burada yararlı olmayacaklar).

Yapmaya çalıştığım şey, veri çerçevesini 'sel' sütunlarına göre gruplara ayırmak ve tüm 'add' sütunlarını bir araya toplamaktır. aşağıdaki gibi yeterince basit aggregate kullanıyor:

# Get the names of all the additional columns 
all.add <- names(orig.df)[!(names(orig.df)) %in% c("sel.1", "sel.2")] 
aggr.df <- aggregate(orig.df[,all.add], 
        by=list(sel.1 = orig.df$sel.1, sel.2 = orig.df$sel.2), sum) 

sorun sonucu 'sel' sütunlarında tarafından sipariş olmasıdır; Her grubun orijinal verilerine göre ilk görünümüne göre sıralanmasını istiyorum. Bu bana doğru sonucu verir iken Birinin basit bir çözüm işaret umuyorum,

## Attempt 1 
# create indices for each row (x) and find the minimum index for each range 
index.df <- aggregate(x = 1:nrow(orig.df), 
         by=list(sel.1 = orig.df$sel.1, sel.2 = orig.df$sel.2), min) 
# Make sure the x vector (indices) are in the right range for aggr.df 
index.order <- (1:nrow(index.df))[order(index.df$x)] 
aggr.df[index.order,] 

## Attempt 2 
# get the unique groups. These are in the right order. 
unique.sel <- unique(orig.df[,c("sel.1", "sel.2")]) 
# use sapply to effectively loop over data and sum additional columns. 
sums <- t(sapply(1:nrow(unique.sel), function (x) { 
    sapply(all.add, function (y) { 
     sum(aggr.df[which(aggr.df$sel.1 == unique.sel$sel.1[x] & 
          aggr.df$sel.2 == unique.sel$sel.2[x]), y]) 
     }) 
})) 
data.frame(unique.sel, sums) 

: Burada

bu işi yapmak benim en iyi çabalarıdır. Çözüm, standart R kurulumu ile gelen paketlerle çalışıyorsa tercih edilebilir.

Ben aggregate ve match belgelerine baktım ama bir cevap (Ben aggregate için bir "keep.original.order" parametresi gibi bir şey için umuyordum sanırım) bulamadık.

Herhangi bir yardım çok takdir edilecektir!


Güncelleme:

unique(data.frame(sapply(names(orig.df), function(x){ 
    if(x %in% c("sel.1", "sel.2")) orig.df[,x] else 
    ave(orig.df[,x], orig.df$sel.1, orig.df$sel.2, FUN=sum)}, 
simplify=FALSE))) 
+1

sayesinde bu belki data.table kullanmanın en güzel çözüm kısadır. R geliştirme ekibinin, toplu olarak 'keep.original.order' parametresini nasıl uygulayacağı daha pişkin? Bu açık bir gözetim gibi görünüyor .. –

cevap

1

Biraz sert okumak için:

İşte

Birkaç gün daha denedikten sonra bulabildiğim en temiz yoludur (durumunda herkes bu rastlar) ama istediğini verir ve açıklığa kavuşturmak için bazı yorumlar ekledim.

# Define the columns you want to combine into the grouping variable 
sel.col <- grepl("^sel", names(orig.df)) 
# Create the grouping variable 
lev <- apply(orig.df[sel.col], 1, paste, collapse=" ") 
# Split and sum up 
data.frame(unique(orig.df[sel.col]), 
      t(sapply(split(orig.df[!sel.col], factor(lev, levels=unique(lev))), 
        apply, 2, sum))) 

çıkış Kısa ve data.table yılında basit bu

sel.1 sel.2 add.1 add.2 
1  5  4 96 84 
2  2  2 175 176 
3  1  5 384 366 
5  2  5 95 89 
6  4  1 174 192 
7  2  4 82 87 
8  5  3 91 98 
10  3  2 189 178 
11  1  4 170 183 
14  1  1 100 91 
17  3  3 81 82 
19  5  5 83 88 
20  2  3 90 96 
5

benziyor. Grupları varsayılan olarak ilk görünüm sırasına göre döndürür.

require(data.table) 
DT = as.data.table(orig.df) 
DT[, list(sum(add.1),sum(add.2)), by=list(sel.1,sel.2)] 

    sel.1 sel.2 V1 V2 
1:  5  4 96 84 
2:  2  2 175 176 
3:  1  5 384 366 
4:  2  5 95 89 
5:  4  1 174 192 
6:  2  4 82 87 
7:  5  3 91 98 
8:  3  2 189 178 
9:  1  4 170 183 
10:  1  1 100 91 
11:  3  3 81 82 
12:  5  5 83 88 
13:  2  3 90 96 

Ve bu büyük veriler için hızlı olacaktır, bu nedenle hız sorunlarını bulursanız kodunuzu değiştirmeniz gerekmez. Aşağıdaki alternatif sözdizimi, hangi sütunların gruplandırılacağını iletmenin en kolay yoludur.

DT[, lapply(.SD,sum), by=c("sel.1","sel.2")] 

    sel.1 sel.2 add.1 add.2 
1:  5  4 96 84 
2:  2  2 175 176 
3:  1  5 384 366 
4:  2  5 95 89 
5:  4  1 174 192 
6:  2  4 82 87 
7:  5  3 91 98 
8:  3  2 189 178 
9:  1  4 170 183 
10:  1  1 100 91 
11:  3  3 81 82 
12:  5  5 83 88 
13:  2  3 90 96 

ya, by da çok sütun adlarının tek virgülle ayrılmış dize olabilir: güncelleme için

DT[, lapply(.SD,sum), by="sel.1,sel.2"]