Как вы (просто) применяете функцию к нескольким подмножествам разной длины в R?

Мне нужно применить функцию к нескольким подмножествам данных разной длины в столбце и создать новый фрейм данных, который включает выходные данные и связанные с ними метаданные.

Как я могу сделать это, не прибегая к циклам? tapply() кажется хорошим местом для начала, но я борюсь с синтаксисом.

Например - у меня есть что-то вроде этого:

block plot id species type response
    1    1  1      w     a      1.5
    1    1  2      w     a      1
    1    1  3      w     a      2
    1    1  4      w     a      1.5
    1    2  5      x     a      5
    1    2  6      x     a      6
    1    2  7      x     a      7
    1    3  8      y     b      10 
    1    3  9      y     b      11
    1    3 10      y     b      9
    1    4 11      z     b      1
    1    4 12      z     b      3
    1    4 13      z     b      2
    2    5 14      w     a      0.5
    2    5 15      w     a      1
    2    5 16      w     a      1.5
    2    6 17      x     a      3
    2    6 18      x     a      2
    2    6 19      x     a      4
    2    7 20      y     b      13 
    2    7 21      y     b      12
    2    7 22      y     b      14
    2    8 23      z     b      2
    2    8 24      z     b      3
    2    8 25      z     b      4
    2    8 26      z     b      2
    2    8 27      z     b      4

И я хочу создать что-то вроде этого:

block plot species type mean.response
    1    1       w    a           1.5
    1    2       x    a           6
    1    3       y    b           10 
    1    4       z    b           2
    2    5       w    a           1
    2    6       x    a           3
    2    7       y    b           13
    2    8       z    b           3

person JKO    schedule 27.10.2020    source источник


Ответы (2)


Попробуй это. Вы можете использовать group_by() для установки группирующих переменных, а затем summarise() для вычисления ожидаемой переменной. Здесь код с использованием dplyr:

library(dplyr)
#Code
newdf <- df %>% group_by(block,plot,species,type) %>% summarise(Mean=mean(response,na.rm=T))

Выход:

# A tibble: 8 x 5
# Groups:   block, plot, species [8]
  block  plot species type   Mean
  <int> <int> <chr>   <chr> <dbl>
1     1     1 w       a       1.5
2     1     2 x       a       6  
3     1     3 y       b      10  
4     1     4 z       b       2  
5     2     5 w       a       1  
6     2     6 x       a       3  
7     2     7 y       b      13  
8     2     8 z       b       3  

Или используя base R (-3 используется, чтобы опустить переменную id в агрегации):

#Base R
newdf <- aggregate(response~.,data=df[,-3],mean,na.rm=T)

Выход:

  block plot species type response
1     1    1       w    a      1.5
2     2    5       w    a      1.0
3     1    2       x    a      6.0
4     2    6       x    a      3.0
5     1    3       y    b     10.0
6     2    7       y    b     13.0
7     1    4       z    b      2.0
8     2    8       z    b      3.0

Некоторые используемые данные:

#Data
df <- structure(list(block = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L), plot = c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 
4L, 4L, 5L, 5L, 5L, 6L, 6L, 6L, 7L, 7L, 7L, 8L, 8L, 8L, 8L, 8L
), id = 1:27, species = c("w", "w", "w", "w", "x", "x", "x", 
"y", "y", "y", "z", "z", "z", "w", "w", "w", "x", "x", "x", "y", 
"y", "y", "z", "z", "z", "z", "z"), type = c("a", "a", "a", "a", 
"a", "a", "a", "b", "b", "b", "b", "b", "b", "a", "a", "a", "a", 
"a", "a", "b", "b", "b", "b", "b", "b", "b", "b"), response = c(1.5, 
1, 2, 1.5, 5, 6, 7, 10, 11, 9, 1, 3, 2, 0.5, 1, 1.5, 3, 2, 4, 
13, 12, 14, 2, 3, 4, 2, 4)), class = "data.frame", row.names = c(NA, 
-27L))
person Duck    schedule 27.10.2020

Используйте любой из них, где ввод dd дан воспроизводимо в примечании в конце:

# 1. aggregate.formula - base R
# Can use just response on left hand side if header doesn't matter.
aggregate(cbind(mean.response = response) ~ block + plot + species + type, dd, mean)

# 2. aggregate.default - base R
v <- c("block", "plot", "species", "type")
aggregate(list(mean.response = dd$response), dd[v], mean)

# 3. sqldf
library(sqldf)
sqldf("select block, plot, species, type, avg(response) as [mean.response]
  from dd group by 1, 2, 3, 4")

# 4. data.table
library(data.table)
v <- c("block", "plot", "species", "type")
as.data.table(dd)[, .(mean.response = mean(response)), by = v]

# 5. doBy - last column of output will be labelled response.mean
library(doBy)
summaryBy(response ~ block + plot + species + type, dd)

Примечание

Ввод в воспроизводимой форме:

dd <- structure(list(block = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L), plot = c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 
4L, 4L, 5L, 5L, 5L, 6L, 6L, 6L, 7L, 7L, 7L, 8L, 8L, 8L, 8L, 8L
), id = 1:27, species = c("w", "w", "w", "w", "x", "x", "x", 
"y", "y", "y", "z", "z", "z", "w", "w", "w", "x", "x", "x", "y", 
"y", "y", "z", "z", "z", "z", "z"), type = c("a", "a", "a", "a", 
"a", "a", "a", "b", "b", "b", "b", "b", "b", "a", "a", "a", "a", 
"a", "a", "b", "b", "b", "b", "b", "b", "b", "b"), response = c(1.5, 
1, 2, 1.5, 5, 6, 7, 10, 11, 9, 1, 3, 2, 0.5, 1, 1.5, 3, 2, 4, 
13, 12, 14, 2, 3, 4, 2, 4)), class = "data.frame", row.names = c(NA, 
-27L))
person G. Grothendieck    schedule 27.10.2020