Agregue um dataframe em uma determinada coluna e exiba outra coluna

Eu tenho um dataframe no R da seguinte 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 

Eu gostaria de agregá-lo seguindo a coluna Score usando a function max

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

Mas eu também gostaria de exibir a coluna Info associada ao valor máximo da coluna Score para cada grupo. Eu não tenho nenhuma idéia de como fazer isso. Minha saída desejada seria:

  Group.1 xy 1 1 3 c 2 2 4 d 

Alguma dica?

Primeiro, você divide os dados usando a split :

 split(z,z$Group) 

Do que, para cada pedaço, selecione a linha com pontuação máxima:

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

Finalmente reduza de volta a um data.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 

Uma linha, sem mágicas, rápido, resultado tem bons nomes =)

Uma solução base R é combinar a saída de aggregate() com uma etapa merge() . Eu acho a interface da fórmula aggregate() um pouco mais útil que a interface padrão, em parte porque os nomes na saída são mais agradáveis, então eu vou usar isso:

O passo aggregate() é

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

e a etapa merge() é simplesmente

 merge(maxs, dat) 

Isso nos dá a saída desejada:

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

Você poderia, é claro, colocar isso em uma única linha (o passo intermediário era mais para exposição):

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

A principal razão pela qual usei a interface de fórmula é que ela retorna um quadro de dados com os names corretos para a etapa de mesclagem; estes são os nomes das colunas do dataset original dat . Precisamos que a saída de aggregate() tenha os nomes corretos para que merge() saiba quais colunas nos frameworks de dados original e agregado correspondem.

A interface padrão dá nomes estranhos, da maneira que você chama:

 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() nessas saídas, mas precisamos fazer mais trabalho informando R quais colunas correspondem.

Aqui está uma solução usando o pacote plyr .

A linha de código a seguir essencialmente diz ao ddply para primeiro agrupar seus dados por Grupo e, em seguida, dentro de cada grupo, retorna um subconjunto onde a Pontuação é igual à pontuação máxima nesse 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 

E, como aponta o @SachaEpskamp, ​​isso pode ser ainda mais simplificado para:

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

(que também tem a vantagem de which.max retornará várias linhas máximas, se houver alguma).

O pacote plyr pode ser usado para isso. Com a function ddply() você pode dividir um quadro de dados em uma ou mais colunas e aplicar uma function e retornar um quadro de dados; em seguida, com a function summarize() você pode usar as colunas do quadro de dados plotdo como variables ​​para fazer o novo quadro de dados/;

 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 

Uma resposta tardia, mas e abordagem usando data.table

 library(data.table) DT < - data.table(dat) DT[, .SD[which.max(Score),], by = Group] 

Ou, se é possível ter mais de uma pontuação igual

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

Observando isso (de ?data.table

.SD é um data.table contendo o Subconjunto de Dados de x para cada grupo, excluindo a (s) coluna (s) do grupo

Para adicionar à resposta de Gavin: antes da fusão, é possível obter um agregado para usar nomes próprios quando não estiver usando a interface de fórmula:

 aggregate(data[,"score", drop=F], list(group=data$group), mean) 

É assim que eu basicamente penso no 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 

Não tenho uma reputação suficientemente alta para comentar a resposta de Gavin Simpson, mas queria avisar que parece haver uma diferença no tratamento padrão de valores omissos entre a syntax padrão e a syntax da 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