Подтвердить что ты не робот

Объединение агрегированных значений обратно в исходный фрейм данных

Один из шаблонов проектирования, которые я использую снова и снова, выполняет "группу за" или "разделять, применять, комбинировать (SAC)" в кадре данных, а затем присоединять агрегированные данные к исходным данным. Это полезно, например, при расчете каждого отклонения графства от среднего значения состояния в кадре данных со многими штатами и округами. Редко мой агрегатный расчет - простое среднее, но это хороший пример. Я часто решаю эту проблему следующим образом:

require(plyr)
set.seed(1)

## set up some data
group1 <- rep(1:3, 4)
group2 <- sample(c("A","B","C"), 12, rep=TRUE) 
values <- rnorm(12)
df <- data.frame(group1, group2, values)

## got some data, so let aggregate

group1Mean <- ddply( df, "group1", function(x) 
                     data.frame( meanValue = mean(x$values) ) )
df <- merge( df, group1Mean )
df

Что создает хорошие агрегированные данные, такие как:

> df
   group1 group2   values meanValue
1       1      A  0.48743 -0.121033
2       1      A -0.04493 -0.121033
3       1      C -0.62124 -0.121033
4       1      C -0.30539 -0.121033
5       2      A  1.51178  0.004804
6       2      B  0.73832  0.004804
7       2      A -0.01619  0.004804
8       2      B -2.21470  0.004804
9       3      B  1.12493  0.758598
10      3      C  0.38984  0.758598
11      3      B  0.57578  0.758598
12      3      A  0.94384  0.758598

Это работает, но есть ли альтернативные способы сделать это, которые улучшают читаемость, производительность и т.д.?

4b9b3361

Ответ 1

Одна строка кода выполняет трюк:

new <- ddply( df, "group1", transform, numcolwise(mean))
new

group1 group2      values    meanValue
1       1      A  0.48742905 -0.121033381
2       1      A -0.04493361 -0.121033381
3       1      C -0.62124058 -0.121033381
4       1      C -0.30538839 -0.121033381
5       2      A  1.51178117  0.004803931
6       2      B  0.73832471  0.004803931
7       2      A -0.01619026  0.004803931
8       2      B -2.21469989  0.004803931
9       3      B  1.12493092  0.758597929
10      3      C  0.38984324  0.758597929
11      3      B  0.57578135  0.758597929
12      3      A  0.94383621  0.758597929

identical(df, new)
[1] TRUE

Ответ 2

Я думаю, что ave() более полезен здесь, чем вызов plyr, который вы показываете (я недостаточно знаком с plyr, чтобы узнать, можете ли вы делать то, что хотите, с plyr напрямую или нет, я был бы удивлен, если вы сможете " t!) или другие базовые R альтернативы (aggregate(), tapply()).:

> with(df, ave(values, group1, FUN = mean))
 [1] -0.121033381  0.004803931  0.758597929 -0.121033381  0.004803931
 [6]  0.758597929 -0.121033381  0.004803931  0.758597929 -0.121033381
[11]  0.004803931  0.758597929

Вы можете использовать within() или transform(), чтобы вставить этот результат непосредственно в df:

> df2 <- within(df, meanValue <- ave(values, group1, FUN = mean))
> head(df2)
  group1 group2     values    meanValue
1      1      A  0.4874291 -0.121033381
2      2      B  0.7383247  0.004803931
3      3      B  0.5757814  0.758597929
4      1      C -0.3053884 -0.121033381
5      2      A  1.5117812  0.004803931
6      3      C  0.3898432  0.758597929
> df3 <- transform(df, meanValue = ave(values, group1, FUN = mean))
> all.equal(df2,df3)
[1] TRUE

И если важно упорядочение:

> head(df2[order(df2$group1, df2$group2), ])
   group1 group2      values    meanValue
1       1      A  0.48742905 -0.121033381
10      1      A -0.04493361 -0.121033381
4       1      C -0.30538839 -0.121033381
7       1      C -0.62124058 -0.121033381
5       2      A  1.51178117  0.004803931
11      2      A -0.01619026  0.004803931

Ответ 3

С точки зрения производительности вы можете выполнять такую ​​же операцию, используя data.table пакет, который встроен в агрегацию и очень быстро благодаря индексам и реализации на основе C. Например, данный df уже существует из вашего примера:


library("data.table")
dt<-as.data.table(df)
setkey(dt,group1)
dt<-dt[,list(group2,values,meanValue=mean(values)),by=group1]
dt
      group1 group2      values   meanValue
 [1,]      1      A  0.82122120  0.18810771
 [2,]      1      C  0.78213630  0.18810771
 [3,]      1      C  0.61982575  0.18810771
 [4,]      1      A -1.47075238  0.18810771
 [5,]      2      B  0.59390132  0.03354688
 [6,]      2      A  0.07456498  0.03354688
 [7,]      2      B -0.05612874  0.03354688
 [8,]      2      A -0.47815006  0.03354688
 [9,]      3      B  0.91897737 -0.20205707
[10,]      3      C -1.98935170 -0.20205707
[11,]      3      B -0.15579551 -0.20205707
[12,]      3      A  0.41794156 -0.20205707

Я не сравнивал это, но по моему опыту это намного быстрее.

Если вы решите спуститься по дороге data.table, которую, я думаю, стоит изучить, если вы работаете с большими наборами данных, вам действительно нужно прочитать документы, потому что есть некоторые отличия от фрейма данных, которые могут вас укусить, если вы не знают о них. Однако, в частности, data.table обычно работает с любой функцией, ожидающей кадр данных, поскольку таблица данных будет требовать, чтобы ее тип был фреймом данных (таблица данных наследуется от фрейма данных).

[Фев. 2011]


[Август 2012] Обновление от Матфея:

Новое в v1.8.2, выпущенное в CRAN в июле 2012 года, := по группам. Это очень похоже на ответ выше, но добавляет новый столбец со ссылкой на dt, поэтому нет копии и нет необходимости в шаге слияния или повторении существующих столбцов для возврата вместе с агрегатом. Вначале нет необходимости setkey, и он справляется с несмежными группами (т.е. Группами, которые не сгруппированы вместе).

Это значительно быстрее для больших наборов данных и имеет простой и короткий синтаксис:

dt <- as.data.table(df)
dt[, meanValue := mean(values), by = group1]

Ответ 4

Не можете ли вы добавить x к функции, которую вы передаете в ddply?

df <- ddply( df, "group1", function(x)
             data.frame( x, meanValue = mean(x$values) ) )

Ответ 5

A dplyr возможность:

library(dplyr)
df %>% 
  group_by(group1) %>%
  mutate(meanValue = mean(values))

Возвращает фрейм данных в исходном порядке. Добавьте arrange(group1) в трубку, если вы хотите заказать "group1".