56 votes

Agréger une trame de données sur une colonne donnée et afficher une autre colonne

J'ai une trame de données en R de la forme suivante:

 > 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
 

Je voudrais l'agréger en suivant la colonne Score utilisant la fonction max

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

  Group.1         x
1       1         3
2       2         4
 

Mais je voudrais également afficher la colonne Info associée à la valeur maximale de la colonne Score pour chaque groupe. Je n'ai aucune idée comment faire ça. Ma sortie souhaitée serait:

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

Un indice?

52voto

Gavin Simpson Points 72349

Une base de R solution est de combiner la sortie de l' aggregate() avec un merge() étape. Je trouve la formule de l'interface d' aggregate() un peu plus utile que l'interface standard, en partie parce que les noms sur la sortie sont plus agréable, donc je vais utiliser:

L' aggregate() étape est

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

et l' merge() étape est tout simplement

merge(maxs, dat)

Cela nous donne le résultat souhaité:

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

Vous pouvez, bien sûr, s'en tenir dans un one-liner (l'intermédiaire de l'étape était plus pour l'exposition):

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

La principale raison, j'ai utilisé la formule de l'interface, c'est qu'il retourne d'un bloc de données avec le bon names pour l'étape de fusion et publipostage; ce sont les noms des colonnes de l'ensemble de données original dat. Nous avons besoin d'une sortie de l' aggregate() d'avoir le bon nom pour qu' merge() sait que les colonnes de l'origine des données agrégées et des cadres de match.

L'interface standard donne des noms impairs, selon la façon dont vous appelez ça:

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

On peut utiliser merge() sur ces sorties, mais nous devons faire plus disant R les colonnes qui correspondent.

38voto

mbq Points 8963

Tout d'abord, vous divisez les données à l'aide de split :

 split(z,z$Group)
 

Ensuite, pour chaque morceau, sélectionnez la ligne avec le score maximum:

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

Enfin, revenez à un data.frame do.call ing rbind :

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

Résultat:

   Group Score Info
1     1     3    c
2     2     4    d
 

Une ligne, pas de sorts magiques, rapide, le résultat a de bons noms =)

15voto

Andrie Points 66979

Voici une solution à l'aide de l' plyr package.

La ligne de code suivante indique essentiellement ddply au premier groupe de vos données par Groupe, puis au sein de chaque groupe retourne un sous-ensemble où le Score est égal au score maximum dans ce groupe.

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

  Group Score Info
1     1     3    c
2     2     4    d

Et, comme @SachaEpskamp points, cela peut encore être simplifiée comme suit:

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

(qui a aussi l'avantage d' which.max renvoie plusieurs lignes max, si il y en a).

5voto

mnel Points 48160

Une réponse tardive, mais et une approche utilisant data.table

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

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

Ou, s'il est possible d'avoir plus d'un score tout aussi élevé

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

Notant que (de ?data.table

.SD est une table de données contenant le sous-ensemble de données de x pour chaque groupe, à l'exclusion des colonnes de groupe

5voto

Sacha Epskamp Points 14956

Le package plyr peut être utilisé pour cela. Avec la fonction ddply() , vous pouvez diviser un bloc de données sur une ou plusieurs colonnes et appliquer une fonction et renvoyer un bloc de données, puis avec la fonction summarize() , vous pouvez utiliser les colonnes du bloc fractionné trame de données en tant que variables pour créer la nouvelle trame de données /;

 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
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X