J'ai un dataframe 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 après la colonne Score
en utilisant la fonction max
> aggregate(data$Score, list(data$Group), max)
Group.1 x
1 1 3
2 2 4
Mais je voudrais aussi 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 désirée serait:
Group.1 x y
1 1 3 c
2 2 4 d
Un indice?
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, ramenez à 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 =)
Une solution de base R consiste à combiner la sortie de aggregate()
avec une étape merge()
. Je trouve l'interface de formule à aggregate()
un peu plus utile que l'interface standard, en partie parce que les noms sur la sortie sont plus agréables, je vais donc utiliser ceci:
L'étape aggregate()
est
maxs <- aggregate(Score ~ Group, data = dat, FUN = max)
et l'étape merge()
est simplement
merge(maxs, dat)
Cela nous donne la sortie souhaitée:
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, coller ceci dans un one-liner (l'étape intermédiaire était plutôt pour l'exposition):
merge(aggregate(Score ~ Group, data = dat, FUN = max), dat)
La principale raison pour laquelle j'ai utilisé l'interface de formule est qu'elle renvoie un bloc de données avec la variable names
correcte pour l'étape de fusion. ce sont les noms des colonnes de l'ensemble de données d'origine dat
. La sortie de aggregate()
doit avoir les noms corrects pour que merge()
sache quelles colonnes des trames de données d'origine et agrégées correspondent.
L'interface standard donne des noms impairs, peu importe comment vous l'appelez:
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
Nous pouvons utiliser merge()
sur ces sorties, mais nous devons faire plus de travail en indiquant à R quelles colonnes correspondent.
Voici une solution utilisant le package plyr
.
La ligne de code suivante indique essentiellement à ddply
de grouper d’abord vos données par groupe, puis, au sein de chaque groupe, renvoie un sous-ensemble où le score est égal au score maximum de 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 le souligne @SachaEpskamp, cela peut encore être simplifié:
ddply(df, .(Group), function(x)x[which.max(x$Score), ])
(ce qui présente également l'avantage que which.max
renverra plusieurs lignes max, s'il y en a).
Le package plyr
peut être utilisé pour cela. Avec la fonction ddply()
, vous pouvez fractionner un bloc de données sur une ou plusieurs colonnes, appliquer une fonction et renvoyer un bloc de données. Avec la fonction summarize()
, vous pouvez également utiliser les colonnes du bloc de données fractionné pour créer le nouveau bloc 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
Pour ajouter à la réponse de Gavin: avant la fusion, il est possible d’obtenir un agrégat pour utiliser les noms propres lorsque l’interface de formule n’est pas utilisée:
aggregate(data[,"score", drop=F], list(group=data$group), mean)
Une réponse tardive, mais et approche en 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 égal.
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 de la ou des colonnes du groupe
C'est ainsi que je base
pense du problème.
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
Je n'ai pas assez de réputation pour commenter la réponse de Gavin Simpson, mais je tenais à signaler qu'il semble y avoir une différence dans le traitement par défaut des valeurs manquantes entre la syntaxe standard et la syntaxe de formule pour 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