Aggrega un dataframe su una determinata colonna e visualizza un’altra colonna

Ho un dataframe in R del seguente modulo:

> 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 

Vorrei aggregarlo seguendo la colonna Score utilizzando la funzione max

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

Ma vorrei anche visualizzare la colonna Info associata al valore massimo della colonna Score per ciascun gruppo. Non ho idea di come farlo. La mia uscita desiderata sarebbe:

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

Qualche suggerimento?

Per prima cosa, dividi i dati usando split :

 split(z,z$Group) 

Quindi, per ogni blocco, seleziona la riga con punteggio massimo:

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

Infine riduci a un data.frame do.call rbind :

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

Risultato:

  Group Score Info 1 1 3 c 2 2 4 d 

Una linea, nessun incantesimo, veloce, il risultato ha un buon nome =)

Una soluzione R di base consiste nel combinare l’output di aggregate() con un passo merge() . Trovo l’interfaccia della formula da aggregate() un po ‘più utile dell’interfaccia standard, in parte perché i nomi sull’output sono più belli, quindi userò questo:

Il passaggio aggregate() è

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

e il passo di merge() è semplicemente

 merge(maxs, dat) 

Questo ci dà l'output desiderato:

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

Potresti, naturalmente, inserire questo in un unico elemento (il passaggio intermedio era più per esposizione):

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

Il motivo principale per cui ho usato l'interfaccia della formula è che restituisce un frame di dati con i names corretti per il passo di unione; questi sono i nomi delle colonne dal dat originale. È necessario che l'output di aggregate() abbia i nomi corretti in modo che merge() sappia quali colonne nei frame di dati originali e aggregati corrispondono.

L'interfaccia standard dà nomi strani, in qualunque modo tu lo chiami:

 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 

Possiamo usare merge() su quegli output, ma dobbiamo fare più lavoro per dire a quali colonne corrispondono.

Ecco una soluzione che utilizza il pacchetto plyr .

La seguente riga di codice indica essenzialmente ddply per raggruppare i dati per gruppo, quindi all’interno di ciascun gruppo restituisce un sottoinsieme in cui il punteggio equivale al punteggio massimo in quel gruppo.

 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, come sottolinea @SachaEpskamp, ​​questo può essere ulteriormente semplificato per:

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

(che ha anche il vantaggio che which.max restituirà più righe which.max , se ce ne sono).

Il pacchetto plyr può essere usato per questo. Con la funzione ddply() è ansible dividere un frame di dati su una o più colonne e applicare una funzione e restituire un frame di dati, quindi con la funzione summarize() è ansible utilizzare le colonne del frame di dati suddiviso come variabili per creare il nuovo data frame /;

 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 

Una risposta tardiva, ma e approccio usando data.table

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

Oppure, se è ansible avere più di un punteggio altrettanto alto

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

Notando che (da ?data.table

.SD è un data.table contenente il sottoinsieme dei dati di x per ciascun gruppo, esclusa la / e colonna / e del gruppo

Per aggiungere alla risposta di Gavin: prima dell’unione, è ansible ottenere l’aggregazione per utilizzare nomi propri quando non si utilizza l’interfaccia della formula:

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

Questo è il modo in cui penso seriamente al 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 

Non ho una reputazione sufficientemente alta per commentare la risposta di Gavin Simpson, ma volevo avvertire che sembra esserci una differenza nel trattamento predefinito dei valori mancanti tra la syntax standard e la syntax della formula per l’ 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