2012-08-08 9 views
8

Ich habe Probleme beim Aggregieren eines Datenrahmens, während die Gruppen in ihrer ursprünglichen Reihenfolge (Reihenfolge basierend auf dem ersten Auftreten im Datenrahmen) beibehalten werden. Ich habe es geschafft, es richtig zu machen, aber ich hatte gehofft, dass es einen einfacheren Weg gibt.Aggregieren von Datenrahmen unter Beibehaltung der ursprünglichen Reihenfolge, in einer einfachen Art und Weise

Hier ist ein Beispieldaten arbeiten:

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) 

Einige Punkte zu beachten: Es gibt zwei Auswahl Spalten, um zu bestimmen, wie die Daten zusammen gruppiert ist. Sie werden gleich sein und ihre Namen sind bekannt. Ich habe nur zwei zusätzliche Spalten in diese Daten eingefügt, aber es könnte mehr geben. Ich habe den Spalten Namen gegeben, die mit 'sel' und 'add' beginnen, um es einfacher zu machen, aber die eigentlichen Daten haben andere Namen (also sind grep Tricks cool, sie sind hier nicht nützlich).

Was ich versuche zu tun, ist das Aggregieren des Datenrahmens in Gruppen basierend auf den 'sel' Spalten, und um alle 'add' Spalten zusammenzufassen. Das ist einfach genug, um mit aggregate wie folgt:

# 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) 

Das Problem ist, dass das Ergebnis durch die ‚sel‘ Spalten angeordnet wird; Ich möchte, dass es nach dem ersten Auftreten jeder Gruppe in den Originaldaten sortiert wird.

Hier sind meine besten Versuche, diese Arbeit zu machen:

## 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) 

Während diese mir das richtige Ergebnis geben, ich hatte gehofft, dass jemand eine einfachere Lösung hinweisen könnte. Es wäre vorzuziehen, wenn die Lösung mit den Paketen arbeitet, die mit der Standard-R-Installation geliefert werden.

Ich habe in der Dokumentation gesucht aggregate und match, aber ich konnte keine Antwort (Ich glaube, ich habe gehofft, für so etwas wie ein „keep.original.order“ Parameter für aggregate) finden.

Jede Hilfe wäre sehr willkommen!


Update: (im Fall stolpert jemand über diese)

Hier ist der sauberste Weg ist, dass ich nach dem Versuch, für ein paar Tage finden konnte:

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

Dank für das Update, das ist vielleicht die schönste Lösung kurz mit data.table. Wie bringt man das R-Entwicklerteam dazu, einen 'keep.original.order'-Parameter für das Aggregat zu implementieren? Das scheint eine klare Aufsicht zu sein. –

Antwort

1

Ein bisschen schwer zu lesen , aber es gibt dir, was du willst und ich fügte einige Kommentare hinzu, um zu klären.

# 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))) 

Die Ausgabe sieht wie folgt aus

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

Es ist kurz und einfach in data.table. Er gibt standardmäßig die Gruppen in der ersten Reihenfolge zurück.

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 

Und dies wird schnell für große Daten, so dass Sie Ihren Code später nicht ändern müssen, wenn Sie Geschwindigkeitsprobleme finden. Die folgende alternative Syntax ist der einfachste Weg, um anzugeben, nach welchen Spalten gruppiert werden soll.

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 

oder by auch eine einzelne Komma getrennte Zeichenfolge von Spaltennamen sein kann, auch:

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