Mittelwert pro Gruppe in einem Datenrahmen

Ich habe einen data.frame und ich muss den Mittelwert pro Gruppe berechnen (dh pro Month , unten).

 Name Month Rate1 Rate2 Aira 1 12 23 Aira 2 18 73 Aira 3 19 45 Ben 1 53 19 Ben 2 22 87 Ben 3 19 45 Cat 1 22 87 Cat 2 67 43 Cat 3 45 32 

Meine gewünschte Ausgabe ist wie folgt, wobei die Werte für Rate1 und Rate2 die Gruppenmittel sind. Bitte ignorieren Sie den Wert, ich habe es für das Beispiel erfunden.

 Name Rate1 Rate2 Aira 23.21 12.2 Ben 45.23 43.9 Cat 33.22 32.2 

   

Diese Art von Operation ist genau das, was aggregate für aggregate entworfen hat:

 d < - read.table(text='Name Month Rate1 Rate2 Aira 1 12 23 Aira 2 18 73 Aira 3 19 45 Ben 1 53 19 Ben 2 22 87 Ben 3 19 45 Cat 1 22 87 Cat 2 67 43 Cat 3 45 32', header=TRUE) aggregate(d[, 3:4], list(d$Name), mean) Group.1 Rate1 Rate2 1 Aira 16.33333 47.00000 2 Ben 31.33333 50.33333 3 Cat 44.66667 54.00000 

Hier aggregieren wir die Spalten 3 und 4 von data.frame d , gruppieren nach d$Name und wenden die mean .


Oder mit einer Formelschnittstelle:

 aggregate(. ~ Name, d[-2], mean) 

Oder verwenden Sie group_by & summarise_at aus dem dplyr Paket:

 library(dplyr) d %>% group_by(Name) %>% summarise_at(vars(-Month), funs(mean(., na.rm=TRUE))) # A tibble: 3 x 3 Name Rate1 Rate2    1 Aira 16.3 47.0 2 Ben 31.3 50.3 3 Cat 44.7 54.0 

Siehe ?summarise_at für die vielen Möglichkeiten, die Variablen anzugeben, auf die ?summarise_at . Hier sagt vars(-Month) alle Variablen außer Month .

Sie können auch das Paket plyr , das irgendwie vielseitiger ist:

 library(plyr) ddply(d, .(Name), summarize, Rate1=mean(Rate1), Rate2=mean(Rate2)) Name Rate1 Rate2 1 Aira 16.33333 47.00000 2 Ben 31.33333 50.33333 3 Cat 44.66667 54.00000 

Eine dritte gute Alternative ist die Verwendung des Pakets data.table , das ebenfalls die class data.frame enthält, aber die Operationen, nach denen Sie suchen, werden viel schneller berechnet.

 library(data.table) mydt < - structure(list(Name = c("Aira", "Aira", "Aira", "Ben", "Ben", "Ben", "Cat", "Cat", "Cat"), Month = c(1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L), Rate1 = c(15.6396600443877, 2.15649279424609, 6.24692918928743, 2.37658797276116, 34.7500663272292, 3.28750138697048, 29.3265553981065, 17.9821839334431, 10.8639802575958), Rate2 = c(17.1680489538369, 5.84231656330206, 8.54330866437461, 5.88415184986176, 3.02064294862551, 17.2053351400752, 16.9552950199166, 2.56058000170089, 15.7496228048122)), .Names = c("Name", "Month", "Rate1", "Rate2"), row.names = c(NA, -9L), class = c("data.table", "data.frame")) 

Nun, um den Mittelwert von Rate1 und Rate2 für alle 3 Monate zu nehmen, für jede Person (Name): Entscheiden Sie zuerst, welche Spalten Sie als Mittelwert verwenden möchten

 colstoavg < - names(mydt)[3:4] 

Jetzt verwenden wir platt, um den Mittelwert über die Spalten zu nehmen, die wir im Durchschnitt darstellen wollen (colstoavg)

 mydt.mean < - mydt[,lapply(.SD,mean,na.rm=TRUE),by=Name,.SDcols=colstoavg] mydt.mean Name Rate1 Rate2 1: Aira 8.014361 10.517891 2: Ben 13.471385 8.703377 3: Cat 19.390907 11.755166 

Ich beschreibe zwei Möglichkeiten, um dies zu tun, eine basierend auf data.table und die andere basierend auf reshape2- Paket. Die data.table Weise hat bereits eine Antwort, aber ich habe versucht, es sauberer und ausführlicher zu machen.

Die Daten sind wie folgt:

  d < - structure(list(Name = structure(c(1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L), .Label = c("Aira", "Ben", "Cat"), class = "factor"), Month = c(1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L), Rate1 = c(12L, 18L, 19L, 53L, 22L, 19L, 22L, 67L, 45L), Rate2 = c(23L, 73L, 45L, 19L, 87L, 45L, 87L, 43L, 32L)), .Names = c("Name", "Month", "Rate1", "Rate2"), class = "data.frame", row.names = c(NA, -9L )) head(d) Name Month Rate1 Rate2 1 Aira 1 12 23 2 Aira 2 18 73 3 Aira 3 19 45 4 Ben 1 53 19 5 Ben 2 22 87 6 Ben 3 19 45 library("reshape2") mym <- melt(d, id = c("Name")) res <- dcast(mym, Name ~ variable, mean) res #Name Month Rate1 Rate2 #1 Aira 2 16.33333 47.00000 #2 Ben 2 31.33333 50.33333 #3 Cat 2 44.66667 54.00000 

Verwenden von data.table:

 # At first, I convert the data.frame to data.table and then I group it setDT(d) d[, .(Rate1 = mean(Rate1), Rate2 = mean(Rate2)), by = .(Name)] # Name Rate1 Rate2 #1: Aira 16.33333 47.00000 #2: Ben 31.33333 50.33333 #3: Cat 44.66667 54.00000 

Es gibt eine andere Möglichkeit, dies zu tun, indem Sie vermeiden, viele Argumente für j in data.table mit einem .SD zu schreiben

 d[, lapply(.SD, mean), by = .(Name)] # Name Month Rate1 Rate2 #1: Aira 2 16.33333 47.00000 #2: Ben 2 31.33333 50.33333 #3: Cat 2 44.66667 54.00000 

Wenn wir nur Rate1 und Rate2 haben wollen, können wir die .SDcols wie folgt verwenden:

 d[, lapply(.SD, mean), by = .(Name), .SDcols = 3:4] # Name Rate1 Rate2 #1: Aira 16.33333 47.00000 #2: Ben 31.33333 50.33333 #3: Cat 44.66667 54.00000 

Hier gibt es eine Vielzahl von Möglichkeiten, dies in Base R zu tun, einschließlich eines alternativen aggregate Ansatzes. Die folgenden Beispiele bedeuten pro Monat, was ich denke, was Sie verlangt haben. Obwohl der gleiche Ansatz für die Rücksendung pro Person verwendet werden könnte:

Verwenden von ave :

 my.data < - read.table(text = ' Name Month Rate1 Rate2 Aira 1 12 23 Aira 2 18 73 Aira 3 19 45 Ben 1 53 19 Ben 2 22 87 Ben 3 19 45 Cat 1 22 87 Cat 2 67 43 Cat 3 45 32 ', header = TRUE, stringsAsFactors = FALSE, na.strings = 'NA') Rate1.mean <- with(my.data, ave(Rate1, Month, FUN = function(x) mean(x, na.rm = TRUE))) Rate2.mean <- with(my.data, ave(Rate2, Month, FUN = function(x) mean(x, na.rm = TRUE))) my.data <- data.frame(my.data, Rate1.mean, Rate2.mean) my.data 

Verwenden by :

 my.data < - read.table(text = ' Name Month Rate1 Rate2 Aira 1 12 23 Aira 2 18 73 Aira 3 19 45 Ben 1 53 19 Ben 2 22 87 Ben 3 19 45 Cat 1 22 87 Cat 2 67 43 Cat 3 45 32 ', header = TRUE, stringsAsFactors = FALSE, na.strings = 'NA') by.month <- as.data.frame(do.call("rbind", by(my.data, my.data$Month, FUN = function(x) colMeans(x[,3:4])))) colnames(by.month) <- c('Rate1.mean', 'Rate2.mean') by.month <- cbind(Month = rownames(by.month), by.month) my.data <- merge(my.data, by.month, by = 'Month') my.data 

Verwenden von lapply und split :

 my.data < - read.table(text = ' Name Month Rate1 Rate2 Aira 1 12 23 Aira 2 18 73 Aira 3 19 45 Ben 1 53 19 Ben 2 22 87 Ben 3 19 45 Cat 1 22 87 Cat 2 67 43 Cat 3 45 32 ', header = TRUE, stringsAsFactors = FALSE, na.strings = 'NA') ly.mean <- lapply(split(my.data, my.data$Month), function(x) c(Mean = colMeans(x[,3:4]))) ly.mean <- as.data.frame(do.call("rbind", ly.mean)) ly.mean <- cbind(Month = rownames(ly.mean), ly.mean) my.data <- merge(my.data, ly.mean, by = 'Month') my.data 

Mit sapply und split :

 my.data < - read.table(text = ' Name Month Rate1 Rate2 Aira 1 12 23 Aira 2 18 73 Aira 3 19 45 Ben 1 53 19 Ben 2 22 87 Ben 3 19 45 Cat 1 22 87 Cat 2 67 43 Cat 3 45 32 ', header = TRUE, stringsAsFactors = FALSE, na.strings = 'NA') my.data sy.mean <- t(sapply(split(my.data, my.data$Month), function(x) colMeans(x[,3:4]))) colnames(sy.mean) <- c('Rate1.mean', 'Rate2.mean') sy.mean <- data.frame(Month = rownames(sy.mean), sy.mean, stringsAsFactors = FALSE) my.data <- merge(my.data, sy.mean, by = 'Month') my.data 

aggregate :

 my.data < - read.table(text = ' Name Month Rate1 Rate2 Aira 1 12 23 Aira 2 18 73 Aira 3 19 45 Ben 1 53 19 Ben 2 22 87 Ben 3 19 45 Cat 1 22 87 Cat 2 67 43 Cat 3 45 32 ', header = TRUE, stringsAsFactors = FALSE, na.strings = 'NA') my.summary <- with(my.data, aggregate(list(Rate1, Rate2), by = list(Month), FUN = function(x) { mon.mean = mean(x, na.rm = TRUE) } )) my.summary <- do.call(data.frame, my.summary) colnames(my.summary) <- c('Month', 'Rate1.mean', 'Rate2.mean') my.summary my.data <- merge(my.data, my.summary, by = 'Month') my.data 

Sie könnten auch die generische function cbind() und lm() ohne den cbind() :

 cbind(lm(d$Rate1~-1+d$Name)$coef,lm(d$Rate2~-1+d$Name)$coef) > [,1] [,2] >d$NameAira 16.33333 47.00000 >d$NameBen 31.33333 50.33333 >d$NameCat 44.66667 54.00000 

Sie können dies auch mithilfe des sqldf Pakets erreichen, wie unten gezeigt:

 library(sqldf) x < - read.table(text='Name Month Rate1 Rate2 Aira 1 12 23 Aira 2 18 73 Aira 3 19 45 Ben 1 53 19 Ben 2 22 87 Ben 3 19 45 Cat 1 22 87 Cat 2 67 43 Cat 3 45 32', header=TRUE) sqldf(" select Name ,avg(Rate1) as Rate1_float ,avg(Rate2) as Rate2_float ,avg(Rate1) as Rate1 ,avg(Rate2) as Rate2 from x group by Name ") # Name Rate1_float Rate2_float Rate1 Rate2 #1 Aira 16.33333 47.00000 16 47 #2 Ben 31.33333 50.33333 31 50 #3 Cat 44.66667 54.00000 44 54 

Ich bin eine aktuelle Konvertierung zu dplyr wie in anderen Antworten gezeigt, aber sqldf ist nett, da die meisten Datenanalysten / Datenwissenschaftler / Entwickler mindestens etwas fließend in SQL haben. Auf diese Weise denke ich, dass es tendenziell für einen allgemein lesbaren Code dplyr als dplyr oder andere Lösungen, die oben dargestellt wurden.

UPDATE: Als ich auf den folgenden Kommentar reactjse, versuchte ich, den Code wie oben gezeigt zu aktualisieren. Das Verhalten war jedoch nicht so, wie ich es erwartet hatte. Es scheint, dass die Spaltendefinition (dh int vs float ) nur ausgeführt wird, wenn der Spaltenalias mit dem ursprünglichen Spaltennamen übereinstimmt. Wenn Sie einen neuen Namen angeben, wird die Aggregatspalte ohne Rundung zurückgegeben.