Агрегировать кадр данных для данного столбца и отобразить другой столбец - PullRequest
52 голосов
/ 09 июня 2011

У меня есть кадр данных в R следующей формы:

> 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

Я бы хотел агрегировать его по столбцу Score, используя функцию max

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

  Group.1         x
1       1         3
2       2         4

Но я также хотел бы отобразить столбец Info, связанный с максимальным значением столбца Score для каждой группы. Я понятия не имею, как это сделать. Мой желаемый результат будет:

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

Есть подсказка?

Ответы [ 8 ]

49 голосов
/ 09 июня 2011

Основным решением R является объединение вывода aggregate() с шагом merge(). Я нахожу интерфейс формулы для aggregate() немного более полезным, чем стандартный интерфейс, отчасти потому, что имена в выводе лучше, поэтому я буду использовать это:

Шаг aggregate()

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

и merge() шаг просто

merge(maxs, dat)

Это дает нам желаемый результат:

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

Конечно, вы могли бы вставить это в одну строку (промежуточный шаг был больше для экспозиции):

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

Основная причина, по которой я использовал интерфейс формулы, заключается в том, что он возвращает фрейм данных с правильным names для шага объединения; это имена столбцов из исходного набора данных dat. Нам нужно, чтобы выходные данные aggregate() имели правильные имена, чтобы merge() знал, какие столбцы в исходных и агрегированных фреймах данных совпадают.

Стандартный интерфейс дает нечетные имена, как бы вы их ни называли:

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

Мы можем использовать merge() на этих выходах, но нам нужно больше работать, сообщая R, какие столбцы совпадают.

36 голосов
/ 09 июня 2011

Сначала вы разделяете данные, используя split:

split(z,z$Group)

Чем для каждого чанка выберите строку с максимальным счетом:

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

Наконец, уменьшите доdata.frame do.call ing rbind:

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

Результат:

  Group Score Info
1     1     3    c
2     2     4    d

Одна строка, без магических заклинаний, быстро, результат имеет хорошие имена =)

13 голосов
/ 09 июня 2011

Вот решение с использованием пакета plyr.

Следующая строка кода по существу говорит ddply сначала сгруппировать ваши данные по группам, а затем в каждой группе возвращается подмножество, где балл равен максимальному баллу в этой группе.

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

  Group Score Info
1     1     3    c
2     2     4    d

И, как указывает @SachaEpskamp, ​​это можно еще более упростить до:

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

(что также имеет преимущество в том, что which.max будет возвращать несколько максимальных строк, если они есть).

4 голосов
/ 28 января 2013

Чтобы добавить к ответу Гевина: до слияния можно получить агрегат для использования собственных имен, если не используется интерфейс формулы:

aggregate(data[,"score", drop=F], list(group=data$group), mean) 
4 голосов
/ 01 ноября 2012

поздний ответ, но и подход с использованием data.table

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

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

Или, если возможно, иметь более одного одинаково высокого балла

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

отмечая, что (от ?data.table

.SD - это таблица данных, содержащая Подмножество данных x для каждой группы, исключая столбец (столбцы) группы

4 голосов
/ 09 июня 2011

Для этого можно использовать пакет plyr. С помощью функции ddply() вы можете разделить фрейм данных на один или несколько столбцов, применить функцию и вернуть фрейм данных, затем с помощью функции summarize() вы можете использовать столбцы разделенного фрейма данных в качестве переменных для создания нового фрейм данных /;

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
3 голосов
/ 09 июня 2011

Вот как я base думаю о проблеме.

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
1 голос
/ 19 июня 2018

У меня недостаточно высокая репутация, чтобы комментировать ответ Гэвина Симпсона, но я хотел предупредить, что, похоже, существует разница в обработке по умолчанию пропущенных значений между стандартным синтаксисом и синтаксисом формулы для 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
...