скажем, у меня есть tibble (или data.table), который состоит из двух столбцов:
a <- tibble(id = rep(c("A", "B"), each = 6), val = c(1, 0, 0, 1 ,0,1,0,0,0,1,1,1))
Кроме того, у меня есть функция myfun, которая принимает на вход числовой вектор произвольной длины и возвращает одно число. Например, вы можете рассматривать myfun как стандартное отклонение.
Теперь я хотел бы создать третий столбец для моего tibble (называемый результатом), который содержит выходные данные myfun, применяемые к суммированным и сгруппированным по идентификатору значениям.
Например, первая запись результата должна содержать mfun(val[1]).
Вторая запись должна содержать myfun(val[1:2]) и так далее.
Я хотел бы реализовать накопленную версию myfun.
Конечно, есть много простых решений, помимо tidyverse, с использованием петель и тому подобного.
Но мне было бы интересно найти решение в рамках tidyverse или в рамках фреймворка data.table.
Любая помощь приветствуется.





Вы можете сделать это так:
library(tidyverse)
a %>%
group_by(id) %>%
mutate(y = map_dbl(seq_along(val),~sd(val[1:.x]))) %>%
ungroup
# # A tibble: 12 x 3
# id val y
# <chr> <dbl> <dbl>
# 1 A 1 NA
# 2 A 0 0.7071068
# 3 A 0 0.5773503
# 4 A 1 0.5773503
# 5 A 0 0.5477226
# 6 A 1 0.5477226
# 7 B 0 NA
# 8 B 0 0.0000000
# 9 B 0 0.0000000
# 10 B 1 0.5000000
# 11 B 1 0.5477226
# 12 B 1 0.5477226
Объяснение
Сначала мы группируем, как часто, с цепочками tidyverse, затем используем mutate, а не summarize, так как мы хотим сохранить те же неагрегированные строки.
Здесь функция map_dbl используется для цикла по вектору конечных индексов. seq_along(val) будет здесь 1:6 для обеих групп.
Используя функции из семейства карт, мы можем использовать нотацию ~, которая предполагает, что первый параметр функции называется .x.
Перебирая эти индексы, мы вычисляем сначала sd(val[1:1]), то есть sd(val[1]), то есть NA, затем sd(val[1:2]) и т. д.
map_dbl по замыслу возвращает вектор doubles, и они складываются в столбец y.
Можно использовать zoo::rollapplyr с динамической шириной (vector containing width). Для подготовки динамической ширины для каждой группы можно использовать 1:n() или seq(n()).
Применим его к функции sd, используя данные, предоставленные OP:
library(dplyr)
library(zoo)
a %>% group_by(id) %>%
mutate(y = rollapplyr(val, 1:n(), sd ))
# # Groups: id [2]
# id val y
# <chr> <dbl> <dbl>
# 1 A 1.00 NA
# 2 A 0 0.707
# 3 A 0 0.577
# 4 A 1.00 0.577
# 5 A 0 0.548
# 6 A 1.00 0.548
# 7 B 0 NA
# 8 B 0 0
# 9 B 0 0
# 10 B 1.00 0.500
# 11 B 1.00 0.548
# 12 B 1.00 0.548
Рад, что смог помочь ;)