columns - Media por grupo en un data.frame
r aggregate multiple columns (8)
Aquí hay una variedad de maneras de hacer esto en la base R
incluye un enfoque aggregate
alternativo. Los ejemplos que figuran a continuación significan por mes, que creo que es lo que solicitó. Aunque, el mismo enfoque podría usarse para devolver los medios por persona:
Usando 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
Usando 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
Usando lapply
y 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
Usando sapply
y 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
Usando 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
Esta pregunta ya tiene una respuesta aquí:
Tengo un data.frame
y necesito calcular la media por grupo (es decir, por Month
, a continuación).
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
Mi salida deseada es como abajo, donde los valores para Rate1
y Rate2
son los medios del grupo. Por favor, ignore el valor, lo inventé para el ejemplo.
Name Rate1 Rate2
Aira 23.21 12.2
Ben 45.23 43.9
Cat 33.22 32.2
Este tipo de operación es exactamente para lo que se diseñó el aggregate
:
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
Aquí agregamos las columnas 3 y 4 de data.frame d
, agrupando por d$Name
y aplicando la función mean
.
O, usando una interfaz de fórmula:
aggregate(. ~ Name, d[-2], mean)
O use group_by
& summarise_at
del paquete dplyr
:
library(dplyr)
d %>%
group_by(Name) %>%
summarise_at(vars(-Month), funs(mean(., na.rm=TRUE)))
# A tibble: 3 x 3
Name Rate1 Rate2
<fct> <dbl> <dbl>
1 Aira 16.3 47.0
2 Ben 31.3 50.3
3 Cat 44.7 54.0
Consulte ?summarise_at
para conocer las diversas formas de especificar las variables para actuar. Aquí, vars(-Month)
dice todas las variables excepto Month
.
También podría usar la función genérica cbind()
y lm()
sin la intercepción:
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
También puede lograr esto usando el paquete sqldf
como se muestra a continuación:
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
Soy un converso reciente a dplyr
como se muestra en otras respuestas, pero sqldf
es bueno ya que la mayoría de los analistas de datos / científicos / desarrolladores de datos tienen al menos algo de fluidez en SQL. De esta manera, creo que tiende a dplyr
código más legible universalmente que dplyr
u otras soluciones presentadas anteriormente.
ACTUALIZACIÓN: Al responder al comentario a continuación, intenté actualizar el código como se muestra arriba. Sin embargo, el comportamiento no fue el esperado. Parece que la definición de la columna (es decir, int
vs float
) solo se lleva a cabo cuando el alias de la columna coincide con el nombre de la columna original. Cuando especifica un nuevo nombre, la columna agregada se devuelve sin redondeo.
También puede usar el paquete plyr
, que de alguna manera es más versátil:
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
Una tercera gran alternativa es usar el paquete data.table
, que también tiene la clase data.frame, pero las operaciones que usted busca se computan mucho más rápido.
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"))
Ahora tome la media de Rate1 y Rate2 para los 3 meses, para cada persona (Nombre): Primero, decida qué columnas quiere tomar con el promedio de
colstoavg <- names(mydt)[3:4]
Ahora utilizamos lapply para tomar la media sobre las columnas que queremos prom (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
Describo dos formas de hacer esto, una basada en data.table y otra basada en el paquete reshape2 . La forma de datos ya tiene una respuesta, pero he tratado de hacerlo más limpio y más detallado.
La información es así:
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
Usando 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
Hay otra forma de hacerlo evitando escribir muchos argumentos para j en data.table usando un .SD
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
si solo queremos tener Rate1 y Rate2, entonces podemos usar los .SDcols de la siguiente manera:
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