Применение нескольких суммарных функций к нескольким переменным по группам в одном вызове
у меня есть следующий фрейм данных
x <- read.table(text = " id1 id2 val1 val2
1 a x 1 9
2 a x 2 4
3 a y 3 5
4 a y 4 9
5 b x 1 7
6 b y 4 4
7 b x 3 9
8 b y 2 8", header = TRUE)
Я хочу вычислить среднее значение val1 и val2, сгруппированных по id1 и id2, и одновременно подсчитать количество строк для каждой комбинации id1-id2. Я могу выполнить каждый расчет отдельно:
# calculate mean
aggregate(. ~ id1 + id2, data = x, FUN = mean)
# count rows
aggregate(. ~ id1 + id2, data = x, FUN = length)
для того, чтобы сделать оба вычисления в одном вызове, я попытался
do.call("rbind", aggregate(. ~ id1 + id2, data = x, FUN = function(x) data.frame(m = mean(x), n = length(x))))
однако, я получаю искаженный вывод вместе с предупреждением:
# m n
# id1 1 2
# id2 1 1
# 1.5 2
# 2 2
# 3.5 2
# 3 2
# 6.5 2
# 8 2
# 7 2
# 6 2
# Warning message:
# In rbind(id1 = c(1L, 2L, 1L, 2L), id2 = c(1L, 1L, 2L, 2L), val1 = list( :
# number of columns of result is not a multiple of vector length (arg 1)
я мог бы использовать пакет plyr, но мой набор данных довольно большой и plyr очень медленный (почти непригодный), когда размер набора данных растет.
как я могу использовать aggregate
или другие функции для выполнения нескольких вычислений в одном вызове?
6 ответов:
вы можете сделать все это в один шаг и получить правильную маркировку:
> aggregate(. ~ id1+id2, data = x, FUN = function(x) c(mn = mean(x), n = length(x) ) ) # id1 id2 val1.mn val1.n val2.mn val2.n # 1 a x 1.5 2.0 6.5 2.0 # 2 b x 2.0 2.0 8.0 2.0 # 3 a y 3.5 2.0 7.0 2.0 # 4 b y 3.0 2.0 6.0 2.0
это создает фрейм данных с двумя столбцами идентификаторов и двумя столбцами матрицы:
str( aggregate(. ~ id1+id2, data = x, FUN = function(x) c(mn = mean(x), n = length(x) ) ) ) 'data.frame': 4 obs. of 4 variables: $ id1 : Factor w/ 2 levels "a","b": 1 2 1 2 $ id2 : Factor w/ 2 levels "x","y": 1 1 2 2 $ val1: num [1:4, 1:2] 1.5 2 3.5 3 2 2 2 2 ..- attr(*, "dimnames")=List of 2 .. ..$ : NULL .. ..$ : chr "mn" "n" $ val2: num [1:4, 1:2] 6.5 8 7 6 2 2 2 2 ..- attr(*, "dimnames")=List of 2 .. ..$ : NULL .. ..$ : chr "mn" "n"
как указал @lord.мусор ниже, это может быть преобразовано в фрейм данных с" простыми " столбцами с помощью
do.call(data.frame, ...)
str( do.call(data.frame, aggregate(. ~ id1+id2, data = x, FUN = function(x) c(mn = mean(x), n = length(x) ) ) ) ) 'data.frame': 4 obs. of 6 variables: $ id1 : Factor w/ 2 levels "a","b": 1 2 1 2 $ id2 : Factor w/ 2 levels "x","y": 1 1 2 2 $ val1.mn: num 1.5 2 3.5 3 $ val1.n : num 2 2 2 2 $ val2.mn: num 6.5 8 7 6 $ val2.n : num 2 2 2 2
это синтаксис для нескольких переменных на LHS:
aggregate(cbind(val1, val2) ~ id1 + id2, data = x, FUN = function(x) c(mn = mean(x), n = length(x) ) )
учитывая это в вопросе:
я мог бы использовать пакет plyr, но мой набор данных довольно большой, и plyr очень медленный (почти непригодный), когда размер набора данных растет.
затем в
data.table
(1.9.4+
) можно попробовать :> DT id1 id2 val1 val2 1: a x 1 9 2: a x 2 4 3: a y 3 5 4: a y 4 9 5: b x 1 7 6: b y 4 4 7: b x 3 9 8: b y 2 8 > DT[ , .(mean(val1), mean(val2), .N), by = .(id1, id2)] # simplest id1 id2 V1 V2 N 1: a x 1.5 6.5 2 2: a y 3.5 7.0 2 3: b x 2.0 8.0 2 4: b y 3.0 6.0 2 > DT[ , .(val1.m = mean(val1), val2.m = mean(val2), count = .N), by = .(id1, id2)] # named id1 id2 val1.m val2.m count 1: a x 1.5 6.5 2 2: a y 3.5 7.0 2 3: b x 2.0 8.0 2 4: b y 3.0 6.0 2 > DT[ , c(lapply(.SD, mean), count = .N), by = .(id1, id2)] # mean over all columns id1 id2 val1 val2 count 1: a x 1.5 6.5 2 2: a y 3.5 7.0 2 3: b x 2.0 8.0 2 4: b y 3.0 6.0 2
Для сравнения таймингов
aggregate
(используется в вопросе и все 3 других ответа) кdata.table
посмотреть этот тест (theagg
иagg.x
случаях).
возможно, вы хотите слияние?
x.mean <- aggregate(. ~ id1+id2, p, mean) x.len <- aggregate(. ~ id1+id2, p, length) merge(x.mean, x.len, by = c("id1", "id2")) id1 id2 val1.x val2.x val1.y val2.y 1 a x 1.5 6.5 2 2 2 a y 3.5 7.0 2 2 3 b x 2.0 8.0 2 2 4 b y 3.0 6.0 2 2
С помощью
dplyr
пакет вы можете достичь этого с помощьюsummarise_all
. С помощью этой summarise-функции вы можете применять другие функции (в данном случаеmean
иn()
) для каждого из негрупповых столбцов:x %>% group_by(id1, id2) %>% summarise_all(funs(mean, n()))
что дает:
id1 id2 val1_mean val2_mean val1_n val2_n 1 a x 1.5 6.5 2 2 2 a y 3.5 7.0 2 2 3 b x 2.0 8.0 2 2 4 b y 3.0 6.0 2 2
если вы не хотите применять функцию(ы) ко всем негрупповым столбцам, вы указываете столбцы, к которым они должны быть применены, или исключаете ненужные с минусом, используя
summarise_at()
функция:# inclusion x %>% group_by(id1, id2) %>% summarise_at(vars(val1, val2), funs(mean, n())) # exclusion x %>% group_by(id1, id2) %>% summarise_at(vars(-val2), funs(mean, n()))