Как сделать условную сумму, которая смотрит только между определенными критериями даты

Скажем, у меня есть данные, которые выглядят как

date, user, items_bought, event_number
2013-01-01, x, 2, 1
2013-01-02, x, 1, 2
2013-01-03, x, 0, 3
2013-01-04, x, 0, 4
2013-01-04, x, 1, 5
2013-01-04, x, 2, 6
2013-01-05, x, 3, 7
2013-01-06, x, 1, 8
2013-01-01, y, 1, 1
2013-01-02, y, 1, 2
2013-01-03, y, 0, 3
2013-01-04, y, 5, 4
2013-01-05, y, 6, 5
2013-01-06, y, 1, 6

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

data.frame(cum_items_bought=unlist(tapply(as.numeric(data$items_bought), data$user, FUN = cumsum)))

вывод из этого выглядит как

date, user, items_bought
2013-01-01, x, 2
2013-01-02, x, 3
2013-01-03, x, 3
2013-01-04, x, 3
2013-01-04, x, 4
2013-01-04, x, 6
2013-01-05, x, 9
2013-01-06, x, 10
2013-01-01, y, 1
2013-01-02, y, 2
2013-01-03, y, 2
2013-01-04, y, 7
2013-01-05, y, 13
2013-01-06, y, 14

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

date, user, cum_items_bought_3_days
2013-01-01, x, 2
2013-01-02, x, 3
2013-01-03, x, 3
2013-01-04, x, 1
2013-01-04, x, 2
2013-01-04, x, 4
2013-01-05, x, 6
2013-01-06, x, 7
2013-01-01, y, 1
2013-01-02, y, 2
2013-01-03, y, 2
2013-01-04, y, 6
2013-01-05, y, 11
2013-01-06, y, 12

person shecode    schedule 03.06.2014    source источник
comment
В первый раз форматирование было лучше. Если у вас есть другие изменения, продолжайте, но оставьте код / ​​данные как есть.   -  person joran    schedule 03.06.2014
comment
Я должен упомянуть. Для каждого пользователя может быть более одной даты (которая упорядочена по эпохе), поэтому я хотел бы подвести итог за 3 дня до этого (включая строки в тот же день, но до интересующей строки)   -  person shecode    schedule 06.06.2014
comment
@ user31260, пожалуйста, поделитесь своим мнением о приведенных ниже ответах, например, удовлетворяют ли они ваши потребности в вычислениях по времени или по любому другому аспекту. Спасибо   -  person David Arenburg    schedule 09.06.2014
comment
Мне пришлось добавить что-то в набор данных, чтобы продемонстрировать, что я хочу, чтобы у пользователя было более одной строки на дату. Приношу свои извинения за то, что для начала не было более четкого примера, я считаю, что люди думают, что я хочу сначала агрегировать на уровне дат, но это не так. Я хочу, чтобы все относилось к X датам, но также имел условие, чтобы номер события для этого пользователя был перед или равным текущему номеру события. В приведенном выше примере показано, что происходит, если 4 января для пользователя x будет несколько строк.   -  person shecode    schedule 10.06.2014


Ответы (7)


Вот решение dplyr, которое даст желаемый результат (14 строк), как указано в вопросе. Обратите внимание, что он заботится о повторяющихся записях даты, например, 2013-01-04 для пользователя x.

# define a custom function to be used in the dplyr chain
myfunc <- function(x){
  with(x, sapply(event_number, function(y) 
    sum(items_bought[event_number <= event_number[y] & date[y] - date <= 2])))
}

require(dplyr)                 #install and load into your library

df %>%
  mutate(date = as.Date(as.character(date))) %>%
  group_by(user) %>%
  do(data.frame(., cum_items_boughtmyfuncdays = myfunc(.))) %>%
  select(-c(items_bought, event_number))

#         date user cum_items_boughtmyfuncdays
#1  2013-01-01    x                       2
#2  2013-01-02    x                       3
#3  2013-01-03    x                       3
#4  2013-01-04    x                       1
#5  2013-01-04    x                       2
#6  2013-01-04    x                       4
#7  2013-01-05    x                       6
#8  2013-01-06    x                       7
#9  2013-01-01    y                       1
#10 2013-01-02    y                       2
#11 2013-01-03    y                       2
#12 2013-01-04    y                       6
#13 2013-01-05    y                      11
#14 2013-01-06    y                      12

В своем ответе я использую пользовательскую функцию myfunc внутри цепочки dplyr. Это делается с помощью оператора do из dplyr. Пользовательская функция передается подмножеству df user группами. Затем он использует sapply для передачи каждого event_number и вычисления суммы items_bought. Последняя строка цепочки dplyr отменяет выбор нежелательных столбцов.

Дайте мне знать, если вы хотите более подробного объяснения.

Изменить после комментария OP:

Если вам нужна большая гибкость для условного суммирования других столбцов, вы можете настроить код следующим образом. Здесь я предполагаю, что остальные столбцы следует суммировать так же, как items_bought. Если это неверно, укажите, как вы хотите суммировать другие столбцы.

Сначала я создаю два дополнительных столбца со случайными числами в данных (я отправлю dput данных внизу своего ответа):

set.seed(99)   # for reproducibility only

df$newCol1 <- sample(0:10, 14, replace=T)
df$newCol2 <- runif(14)

df
#         date user items_bought event_number newCol1     newCol2
#1  2013-01-01    x            2            1       6 0.687800094
#2  2013-01-02    x            1            2       1 0.640190769
#3  2013-01-03    x            0            3       7 0.357885360
#4  2013-01-04    x            0            4      10 0.102584999
#5  2013-01-04    x            1            5       5 0.097790922
#6  2013-01-04    x            2            6      10 0.182886256
#7  2013-01-05    x            3            7       7 0.227903474
#8  2013-01-06    x            1            8       3 0.080524150
#9  2013-01-01    y            1            1       3 0.821618422
#10 2013-01-02    y            1            2       1 0.591113977
#11 2013-01-03    y            0            3       6 0.773389019
#12 2013-01-04    y            5            4       5 0.350085977
#13 2013-01-05    y            6            5       2 0.006061323
#14 2013-01-06    y            1            6       7 0.814506223

Затем вы можете изменить myfunc, чтобы он принимал 2 аргумента вместо 1. Первый аргумент останется как подмножество data.frame (представленный . внутри цепочки dplyr и x в определении функции myfunc), а второй аргумент - на myfunc укажет столбец для суммирования (colname).

myfunc <- function(x, colname){
  with(x, sapply(event_number, function(y) 
    sum(x[event_number <= event_number[y] & date[y] - date <= 2, colname])))
}

Затем вы можете использовать myfunc несколько раз, если хотите условно суммировать несколько столбцов:

df %>%
  mutate(date = as.Date(as.character(date))) %>%
  group_by(user) %>%
  do(data.frame(., cum_items_bought_3_days = myfunc(., "items_bought"),
                   newCol1Sums = myfunc(., "newCol1"),            
                   newCol2Sums = myfunc(., "newCol2"))) %>%
select(-c(items_bought, event_number, newCol1, newCol2))

#         date user cum_items_bought_3_days newCol1Sums newCol2Sums
#1  2013-01-01    x                       2           6   0.6878001
#2  2013-01-02    x                       3           7   1.3279909
#3  2013-01-03    x                       3          14   1.6858762
#4  2013-01-04    x                       1          18   1.1006611
#5  2013-01-04    x                       2          23   1.1984520
#6  2013-01-04    x                       4          33   1.3813383
#7  2013-01-05    x                       6          39   0.9690510
#8  2013-01-06    x                       7          35   0.6916898
#9  2013-01-01    y                       1           3   0.8216184
#10 2013-01-02    y                       2           4   1.4127324
#11 2013-01-03    y                       2          10   2.1861214
#12 2013-01-04    y                       6          12   1.7145890
#13 2013-01-05    y                      11          13   1.1295363
#14 2013-01-06    y                      12          14   1.1706535

Теперь вы создали условные суммы столбцов items_bought, newCol1 и newCol2. Вы также можете опустить любую из сумм в цепочке dplyr или добавить дополнительные столбцы для суммирования.

Изменить # 2 после комментария OP:

Чтобы вычислить совокупную сумму различных (уникальных) товаров, купленных одним пользователем, вы можете определить вторую настраиваемую функцию myfunc2 и использовать ее внутри цепочки dplyr. Эта функция также является гибкой, как myfunc, так что вы можете определить столбцы, к которым вы хотите применить функцию.

Тогда код будет:

myfunc <- function(x, colname){
  with(x, sapply(event_number, function(y) 
    sum(x[event_number <= event_number[y] & date[y] - date <= 2, colname])))
}

myfunc2 <- function(x, colname){
  cumsum(sapply(seq_along(x[[colname]]), function(y) 
    ifelse(!y == 1 & x[y, colname] %in% x[1:(y-1), colname], 0, 1)))
}

require(dplyr)                 #install and load into your library

dd %>%
  mutate(date = as.Date(as.character(date))) %>%
  group_by(user) %>%
  do(data.frame(., cum_items_bought_3_days = myfunc(., "items_bought"),
                   newCol1Sums = myfunc(., "newCol1"),
                   newCol2Sums = myfunc(., "newCol2"),
                   distinct_items_bought = myfunc2(., "items_bought"))) %>%   
  select(-c(items_bought, event_number, newCol1, newCol2))

Вот данные, которые я использовал:

dput(df)
structure(list(date = structure(c(1L, 2L, 3L, 4L, 4L, 4L, 5L, 
6L, 1L, 2L, 3L, 4L, 5L, 6L), .Label = c("2013-01-01", "2013-01-02", 
"2013-01-03", "2013-01-04", "2013-01-05", "2013-01-06"), class = "factor"), 
user = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 
2L, 2L, 2L, 2L), .Label = c(" x", " y"), class = "factor"), 
items_bought = c(2L, 1L, 0L, 0L, 1L, 2L, 3L, 1L, 1L, 1L, 
0L, 5L, 6L, 1L), event_number = c(1L, 2L, 3L, 4L, 5L, 6L, 
7L, 8L, 1L, 2L, 3L, 4L, 5L, 6L), newCol1 = c(6L, 1L, 7L, 
10L, 5L, 10L, 7L, 3L, 3L, 1L, 6L, 5L, 2L, 7L), newCol2 = c(0.687800094485283, 
0.640190769452602, 0.357885359786451, 0.10258499882184, 0.0977909218054265, 
0.182886255905032, 0.227903473889455, 0.0805241498164833, 
0.821618422167376, 0.591113976901397, 0.773389018839225, 
0.350085976999253, 0.00606132275424898, 0.814506222726777
)), .Names = c("date", "user", "items_bought", "event_number", 
"newCol1", "newCol2"), row.names = c(NA, -14L), class = "data.frame")
person talat    schedule 10.06.2014
comment
Это очень хорошо, спасибо. Я хотел бы сделать функцию более гибкой, поскольку в моем наборе данных у меня есть несколько столбцов, похожих на items_bought, которые я хочу делать суммы / подсчеты и т. Д., Есть ли способ сделать это? - person shecode; 11.06.2014
comment
@ user31260, хотите ли вы рассчитать суммы для других столбцов точно так же, как для cum_items_bought_3_days? Если нет, не могли бы вы более подробно описать, как следует резюмировать другие функции? - person talat; 11.06.2014
comment
для некоторых столбцов - да, но для некоторых других я могу, например, подсчитать количество различных типов купленных товаров и т. д. - person shecode; 11.06.2014
comment
Я отредактировал свой ответ, чтобы сделать myfunc более гибким. Теперь вы можете указать любые столбцы, которые хотите суммировать, на тех же условиях, что и для items_bought. Если вы хотите подсчитать количество различных типов купленных товаров, вы имеете в виду обычную совокупную сумму или также при условии, что дата находится в пределах 3 дней от текущей строки? - person talat; 11.06.2014
comment
было бы идеально иметь гибкость для выполнения обоих этих примеров в рамках одной функции. большое спасибо за ваш ответ :) - person shecode; 11.06.2014
comment
А что вы считаете отдельным типом покупаемых товаров? Вы имеете в виду уникальные числа в столбце items_bought? Кажется, они представляют количество предметов, а не какие (отдельные) предметы? - person talat; 11.06.2014
comment
@ user31260 не могли бы вы отредактировать свой вопрос, включив в него пример того, как вы хотите, чтобы результат выглядел после подсчета различных типов купленных товаров? Мне непонятно, как вы этого хотите, учитывая образцы данных. - person talat; 11.06.2014
comment
Я понимаю, что вы имеете в виду, но это более общий случай. Так что подсчета различных уникальных чисел в столбце items_bought будет достаточно. Я могу преобразовать логику оттуда - person shecode; 11.06.2014

Я хотел бы предложить дополнительный data.table подход в сочетании с zoo пакетной rollapplyr функцией

Во-первых, мы будем агрегировать items_bought столбца на user для каждого уникального date (как вы указали, для каждого пользователя может быть более одной уникальной даты)

library(data.table)
data <- setDT(data)[, lapply(.SD, sum), by = c("user", "date"), .SDcols = "items_bought"]

Затем мы вычислим rollapplyr в сочетании с sum и partial = TRUE, чтобы скрыть поля (спасибо за совет @G. Grothendieck) с интервалом в 3 дня

library(zoo)
data[, cum_items_bought_3_days := lapply(.SD, rollapplyr, 3, sum, partial = TRUE), .SDcols = "items_bought", by = user]

#     user       date items_bought cum_items_bought_3_days
#  1:    x 2013-01-01            2                       2
#  2:    x 2013-01-02            1                       3
#  3:    x 2013-01-03            0                       3
#  4:    x 2013-01-04            0                       1
#  5:    x 2013-01-05            3                       3
#  6:    x 2013-01-06            1                       4
#  7:    y 2013-01-01            1                       1
#  8:    y 2013-01-02            1                       2
#  9:    y 2013-01-03            0                       2
# 10:    y 2013-01-04            5                       6
# 11:    y 2013-01-05            6                      11
# 12:    y 2013-01-06            1                      12

Это набор данных, который я использовал

data <- structure(list(date = structure(c(15706, 15707, 15708, 15709, 15710, 15711, 15706, 15707, 15708, 15709, 15710, 15711), class = "Date"), user = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L), .Label = c(" x", " y"), class = "factor"), items_bought = c(2L, 1L, 0L, 0L, 3L, 1L, 1L, 1L, 0L, 5L, 6L, 1L)), .Names = c("date", "user", "items_bought"), row.names = c(NA, -12L), class = "data.frame")
person David Arenburg    schedule 08.06.2014
comment
Обратите внимание, что rollapply поддерживает partial=TRUE и существует rollapplyr, поэтому мы можем записать строку rollsum как data[, cum_items_bought_3_days := lapply(.SD, rollapplyr, 3, sum, partial = TRUE), .SDcols = "items_bought", by = user], и в этом случае мы закончили на этом этапе. - person G. Grothendieck; 09.06.2014
comment
Спасибо, @Grothendieck, я отредактировал ответ. Я думал избежать роллаплинга, потому что в документации пакета zoo указано, что rollsum более оптимизирован для скорости, но я, по всей видимости, пропустил параметр partial = T - person David Arenburg; 09.06.2014
comment
@beginneR, это потому, что он хотел удалить повторяющиеся даты (которые у него были в исходных данных), см. начало моего объяснения - person David Arenburg; 10.06.2014
comment
Хорошо, я не понял этого, прочитав вопрос. Тогда я удалю свой комментарий. - person talat; 10.06.2014

Вот довольно простой способ:

# replicate your data, shifting the days ahead by your required window,
# and rbind into a single data frame
d <- do.call(rbind,lapply(0:2, function(x) transform(data,date=date+x)))

# use aggregate to add it together, subsetting out "future" days
aggregate(items_bought~date+user,subset(d,date<=max(data$date)),sum)
         date user items_bought
1  2013-01-01    x            2
2  2013-01-02    x            3
3  2013-01-03    x            3
4  2013-01-04    x            1
5  2013-01-05    x            3
6  2013-01-06    x            4
7  2013-01-01    y            1
8  2013-01-02    y            2
9  2013-01-03    y            2
10 2013-01-04    y            6
11 2013-01-05    y           11
12 2013-01-06    y           12
person James    schedule 06.06.2014
comment
Привет. Спасибо за ваш ответ. Я должен был быть более подробным с моим вопросом. На самом деле у меня есть несколько строк для тех же дат, что и у меня на уровне секунд. Я хотел получить скользящую сумму / среднее значение и т. Д., Но в течение x дней. Так что я не думаю, что переключение сработает. Это хорошее решение, если бы мои данные были полностью агрегированы. Спасибо - person shecode; 06.06.2014
comment
@ user31260 Вы можете не просто преобразовать ваше подробное время в переменную класса Date, или секунды важны? Шаг aggregate суммирует все строки, поэтому несколько строк не проблема. Хотя, в зависимости от размера ваших данных, перед репликацией может быть лучше агрегировать. - person James; 06.06.2014
comment
спасибо, но это не сработает для меня, поскольку я на самом деле не хочу предварительно агрегировать вещи, я хочу, чтобы сумма учитывала текущий день перед текущей строкой и т. д. - person shecode; 10.06.2014

Следующее выглядит действительным:

unlist(lapply(split(data, data$user), 
              function(x) {
                 ave(x$items_bought, 
                 cumsum(c(0, diff(x$date)) >= 3), FUN = cumsum) 
              }))   
#x1  x2  x3  x4  y1  y2  y3  y4 
# 2   3   3   4   1   6   6   7

Где data:

data = structure(list(date = structure(c(15706, 15707, 15710, 15711, 
15706, 15707, 15710, 15711), class = "Date"), user = structure(c(1L, 
1L, 1L, 1L, 2L, 2L, 2L, 2L), .Label = c(" x", " y"), class = "factor"), 
    items_bought = c(2L, 1L, 3L, 1L, 1L, 5L, 6L, 1L)), .Names = c("date", 
"user", "items_bought"), row.names = c(NA, -8L), class = "data.frame")
person alexis_laz    schedule 03.06.2014
comment
Спасибо большое. вы можете объяснить, что этот бит здесь cumsum (c (0, diff (x $ date)) ›= 3),? - person shecode; 05.06.2014
comment
Хорошо, так что это не сработает, если на самом деле не будет трехдневного перерыва в данных. до тех пор он ведет кумулятивное суммирование. Я пытаюсь получить сумму окна - person shecode; 05.06.2014
comment
данные = структура (список (дата = структура (c (15706, 15707, 15708, 15709, 15710, 15711, 15706, 15707, 15708, 15709, 15710, 15711), class = Date), user = structure (c (1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L), .Label = c (x, y), class = factor), items_bought = c (2L, 1L, 0L, 0L, 3L, 1L, 1L, 1L, 0L, 5L, 6L, 1L)), .Names = c (дата, пользователь, items_bought), row.names = c (NA, -12L), class = data.frame) и ответ должен быть: 2,3,3,1,3,4,1,2,2,6,11,12 - person shecode; 05.06.2014
comment
Я добавил кое-что в образец набора данных, чтобы было понятнее - person shecode; 05.06.2014

Вот подход, в котором используется не cumsum, а вложенный lapply. Первый проходит по пользователям, а затем для каждого пользователя второй lapply создает желаемый фрейм данных, суммируя все товары, купленные в течение последних 2 дней каждой даты. Обратите внимание, что если data$date не были отсортированы, сначала их нужно было бы отсортировать в порядке возрастания.

data <- structure(list(
    date = structure(c(15706, 15707, 15708, 15709, 15710, 15711, 
        15706, 15707, 15708, 15709, 15710, 15711), class = "Date"), 
    user = c("x", "x", "x", "x", "x", "x", "y", "y", "y", "y", "y", "y"),
    items_bought = c(2L, 1L, 0L, 0L, 3L, 1L, 1L, 1L, 0L, 5L, 6L, 1L)),
    .Names = c("date", "user", "items_bought"),
    row.names = c(NA, -12L),
    class = "data.frame")

do.call(rbind, lapply(unique(data$user),
   function(u) {
       subd <- subset(data, user == u)
       do.call(rbind, lapply(subd$date, 
           function(x) data.frame(date = x, 
               user = u, items_bought = 
               sum(subd[subd$date %in% (x - 2):x, "items_bought"]))))
}))

Изменить

Чтобы решить проблему наличия нескольких временных меток на каждый день (более одной строки на дату), я бы сначала суммировал, суммируя все товары, купленные в течение каждого дня в один и тот же день. Вы можете сделать это, например, используя встроенную функцию aggregate, но если ваши данные слишком велики, вы также можете использовать data.table для скорости. Я назову исходный фрейм данных (с более чем 1 строкой на дату) predata, а агрегированный (1 строка на дату) data. Итак, позвонив

predt <- data.table(predata)
setkey(predt, date, user)
data <- predt[, list(items_bought = sum(items_bought)), by = key(predt)]

вы получите фрейм данных, содержащий по одной строке на дату и столбцы date, user, items_bought. Теперь я думаю, что следующий способ будет быстрее, чем вложенный lapply выше, но я не уверен, так как я не могу проверить его на ваших данных. Я использую data.table, потому что он должен быть быстрым (при правильном использовании, в чем я не уверен). Внутренний цикл будет заменен функцией f. Я не знаю, есть ли более аккуратный способ избежать этой функции и заменить двойной цикл только одним вызовом data.table или как написать вызов data.table, который выполнялся бы быстрее.

library(data.table)
dt <- data.table(data)
setkey(dt, user)
f <- function(d, u) {
    do.call(rbind, lapply(d$date, function(x) data.frame(date = x,
        items_bought = d[date %in% (x - 2):x, sum(items_bought)])))
}
data <- dt[, f(.SD, user), by = user]

Другой способ, который не использует data.table, при условии, что у вас достаточно оперативной памяти (опять же, я не знаю размер ваших данных), - хранить в векторе товары, купленные за 1 день до этого, а затем товары, купленные за 2 дня. перед в другом векторе и т. д., и подвести их в конце. Что-то вроде

sumlist <- vector("list", 2) # this will hold one vector, which contains items 
    # bought 1 or 2 days ago
for (i in 1:2) {
    # tmpstr will be used to find the items that a given user bought i days ago
    tmpstr <- paste(data$date - i, data$user, sep = "|")
    tmpv <- data$items_bought[
        match(tmpstr, paste(data$date, data$user, sep = "|"))]
    # if a date is not in the original data, assume no purchases
    tmpv[is.na(tmpv)] <- 0
    sumlist[[i]] <- tmpv
}
# finally, add up items bought in the past as well as the present day
data$cum_items_bought_3_days <- 
    rowSums(as.data.frame(sumlist)) + data$items_bought

Последнее, что я хотел бы попробовать, - это распараллелить вызовы lapply, например используя вместо этого функцию mclapply или переписав код, используя параллельную функциональность foreach или plyr. В зависимости от мощности вашего ПК и размера задачи это может превзойти одноядерную производительность data.table ...

person konvas    schedule 06.06.2014
comment
Спасибо за вашу попытку. Это работает, однако мой набор данных довольно велик и его очень медленно реализовать. Можно ли улучшить производительность? Кроме того, мой фактический набор данных на самом деле имеет много отметок времени с интервалом в несколько секунд, возможно ли сделать эту кумулятивную сумму для каждой строки, где отметки времени / даты лежат в пределах критериев даты? (Т.е. у меня более одной строки на дату) - person shecode; 06.06.2014

Кажется, что пакеты xts и zoo содержат функции, которые делают то, что вы хотите, хотя у вас могут быть те же проблемы с размером вашего фактического набора данных, что и с ответом @alexis_laz. Использование функций из xts ответа на этот вопрос, похоже, помогает.

Сначала я взял код из ответа, на который ссылаюсь выше, и убедился, что он работает только для одного user. Я включаю функцию apply.daily, потому что на основании ваших правок / комментариев я считаю, что у вас есть несколько наблюдений в течение нескольких дней для некоторых пользователей - я добавил дополнительную строку в набор данных игрушек, чтобы отразить это.

# Make dataset with two observations for one date for "y" user
dat <- structure(list(
    date = structure(c(15706, 15707, 15708, 15709, 15710, 15711, 
        15706, 15707, 15708, 15709, 15710, 15711, 15711), class = "Date"), 
    user = c("x", "x", "x", "x", "x", "x", "y", "y", "y", "y", "y", "y", "y"),
    items_bought = c(2L, 1L, 0L, 0L, 3L, 1L, 1L, 1L, 0L, 5L, 6L, 1L, 0L)),
    .Names = c("date", "user", "items_bought"),
    row.names = c(NA, -13L),
    class = "data.frame")

# Load xts package (also loads zoo)
require(xts)

# See if this works for one user
dat1 = subset(dat, user == "y")
# Create "xts" object for use with apply.daily()
dat1.1 = xts(dat1$items_bought, dat1$date)
dat2 = apply.daily(dat1.1, sum)
# Now use rollapply with a 3-day window
# The "partial" argument appears to only work with zoo objects, not xts
sum.itemsbought = rollapply(zoo(dat2), 3, sum, align = "right", partial = TRUE)

Я думал, что результат может выглядеть лучше (больше как пример вывода из вашего вопроса). Я мало работал с zoo объектами, но ответ на этот вопрос дал мне несколько советов по помещению информации в data.frame.

data.frame(Date=time(sum.itemsbought), sum.itemsbought, row.names=NULL)

Как только я разработал это для одного user, было несложно расширить это на весь набор данных игрушек. Вот где скорость может стать проблемой. Я использую lapply и do.call для этого шага.

allusers = lapply(unique(dat$user), function(x) {
    dat1 = dat[dat$user == x,]
    dat1.1 = xts(dat1$items_bought, dat1$date)
    dat2 = apply.daily(dat1.1, sum)
    sum.itemsbought = rollapply(zoo(dat2), 3, sum, align = "right", partial = TRUE)
    data.frame(Date=time(sum.itemsbought), user = x, sum.itemsbought, row.names=NULL)
} )
do.call(rbind, allusers)
person aosmith    schedule 06.06.2014

Мне больше нравится ответ Джеймса, но вот альтернатива:

with(data,{
  sapply(split(data,user),function(x){
    sapply(x$date,function(y) sum(x$items_bought[x$date %in% c(y,y-1,y-2)]))
  })
})
person userNaN    schedule 07.06.2014
comment
Привет. На самом деле это не дает мне нужного результата (как показано выше). спасибо за вашу попытку. он воспроизводит суммы за тот же день. но я хочу быть в порядке данных, как показано выше - person shecode; 09.06.2014
comment
Предположим, что у нас есть номер события в виде столбца, пронумерованного 1: 6 для пользователя x и 1: 6 для пользователя y. Итак, мы хотим, чтобы логика также сообщала, где номер события меньше, чем текущая строка. - person shecode; 09.06.2014
comment
Просто агрегировать, сортировать и объединять данные? - person userNaN; 10.06.2014
comment
например назначьте вышеприведенный вывод переменной z и добавьте его в правильно упорядоченный набор данных, используя c(z[,1],z[,2]). - person userNaN; 10.06.2014