Agregar un dataframe en una columna dada y mostrar otra columna


Tengo un dataframe en R de la siguiente forma:

> head(data)
  Group Score Info
1     1     1    a
2     1     2    b
3     1     3    c
4     2     4    d
5     2     3    e
6     2     1    f

Me gustaría agregarlo siguiendo la columna Score usando la función max

> aggregate(data$Score, list(data$Group), max)

  Group.1         x
1       1         3
2       2         4

Pero también me gustaría mostrar la columna Info asociada al valor máximo de la columna Score para cada grupo. No tengo idea de cómo hacer esto. Mi salida deseada sería:

  Group.1         x        y
1       1         3        c
2       2         4        d

¿Alguna pista?

Author: Bulat, 2011-06-09

8 answers

Primero, divide los datos usando split:

split(z,z$Group)

Que, para cada trozo, seleccione la fila con puntuación máxima:

lapply(split(z,z$Group),function(chunk) chunk[which.max(chunk$Score),])

Finalmente reducir de nuevo a un dato.frame do.call ing rbind:

do.call(rbind,lapply(split(z,z$Group),function(chunk) chunk[which.max(chunk$Score),]))

Resultado:

  Group Score Info
1     1     3    c
2     2     4    d

Una línea, sin hechizos mágicos, rápido, el resultado tiene buenos nombres =)

 33
Author: mbq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-09 08:30:00

Una solución base R es combinar la salida de aggregate() con un paso merge(). Encuentro la interfaz de fórmula a aggregate() un poco más útil que la interfaz estándar, en parte porque los nombres en la salida son más agradables, así que usaré eso:

El paso aggregate() es

maxs <- aggregate(Score ~ Group, data = dat, FUN = max)

Y el paso merge() es simplemente

merge(maxs, dat)

Esto nos da la salida deseada:

R> maxs <- aggregate(Score ~ Group, data = dat, FUN = max)
R> merge(maxs, dat)
  Group Score Info
1     1     3    c
2     2     4    d

Usted podría, por supuesto, pegar esto en una sola línea (el paso intermedio fue más para exposición):

merge(aggregate(Score ~ Group, data = dat, FUN = max), dat)

La razón principal por la que utilicé la interfaz de fórmula es que devuelve un marco de datos con el names correcto para el paso de fusión; estos son los nombres de las columnas del conjunto de datos original dat. Necesitamos que la salida de aggregate() tenga los nombres correctos para que merge() sepa qué columnas en los marcos de datos originales y agregados coinciden.

La interfaz estándar da nombres impares, como se llame:

R> aggregate(dat$Score, list(dat$Group), max)
  Group.1 x
1       1 3
2       2 4
R> with(dat, aggregate(Score, list(Group), max))
  Group.1 x
1       1 3
2       2 4

Podemos usar merge() en aquellos salidas, pero tenemos que hacer más trabajo diciendo R qué columnas coinciden.

 47
Author: Gavin Simpson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-09 08:16:25

Aquí hay una solución usando el paquete plyr.

La siguiente línea de código esencialmente le dice a ddply que primero agrupe sus datos por Grupo, y luego dentro de cada grupo devuelve un subconjunto donde la Puntuación es igual a la puntuación máxima en ese grupo.

library(plyr)
ddply(data, .(Group), function(x)x[x$Score==max(x$Score), ])

  Group Score Info
1     1     3    c
2     2     4    d

Y, como señala @SachaEpskamp, esto se puede simplificar aún más a:

ddply(df, .(Group), function(x)x[which.max(x$Score), ])

(que también tiene la ventaja de que which.max devolverá múltiples líneas máximas, si hay alguna).

 13
Author: Andrie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-09 08:20:08

El paquete plyr se puede usar para esto. Con la función ddply() puede dividir un marco de datos en una o más columnas y aplicar una función y devolver un marco de datos, luego con la función summarize() puede usar las columnas del marco de datos dividido como variables para hacer el nuevo marco de datos/;

dat <- read.table(textConnection('Group Score Info
1     1     1    a
2     1     2    b
3     1     3    c
4     2     4    d
5     2     3    e
6     2     1    f'))

library("plyr")

ddply(dat,.(Group),summarize,
    Max = max(Score),
    Info = Info[which.max(Score)])
  Group Max Info
1     1   3    c
2     2   4    d
 4
Author: Sacha Epskamp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-09 07:53:05

Una respuesta tardía, pero y enfoque usando data.table

library(data.table)
DT <- data.table(dat)

DT[, .SD[which.max(Score),], by = Group]

O, si es posible tener más de una puntuación igual de alta

DT[, .SD[which(Score == max(Score)),], by = Group]

Observando que (de ?data.table

.SD es un dato.tabla que contiene el Subconjunto de datos de x para cada grupo, excluyendo la(s) columna (s) de grupo

 4
Author: mnel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-31 22:55:47

Para agregar a la respuesta de Gavin: antes de la fusión, es posible obtener aggregate para usar nombres propios cuando no se usa la interfaz de fórmula:

aggregate(data[,"score", drop=F], list(group=data$group), mean) 
 4
Author: Dan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-18 14:56:33

Así es como yo base ically pensar en el problema.

my.df <- data.frame(group = rep(c(1,2), each = 3), 
        score = runif(6), info = letters[1:6])
my.agg <- with(my.df, aggregate(score, list(group), max))
my.df.split <- with(my.df, split(x = my.df, f = group))
my.agg$info <- unlist(lapply(my.df.split, FUN = function(x) {
            x[which(x$score == max(x$score)), "info"]
        }))

> my.agg
  Group.1         x info
1       1 0.9344336    a
2       2 0.7699763    e
 3
Author: Roman Luštrik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-09 08:17:27

No tengo una reputación lo suficientemente alta como para comentar la respuesta de Gavin Simpson, pero quería advertir que parece haber una diferencia en el tratamiento predeterminado de los valores faltantes entre la sintaxis estándar y la sintaxis de fórmula para aggregate.

#Create some data with missing values 
a<-data.frame(day=rep(1,5),hour=c(1,2,3,3,4),val=c(1,NA,3,NA,5))
  day hour val
1   1    1   1
2   1    2  NA
3   1    3   3
4   1    3  NA
5   1    4   5

#Standard syntax
aggregate(a$val,by=list(day=a$day,hour=a$hour),mean,na.rm=T)
  day hour   x
1   1    1   1
2   1    2 NaN
3   1    3   3
4   1    4   5

#Formula syntax.  Note the index for hour 2 has been silently dropped.
aggregate(val ~ hour + day,data=a,mean,na.rm=T)
  hour day val
1    1   1   1
2    3   1   3
3    4   1   5
 0
Author: John,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-18 21:36:12