Периодическая сумма с течением времени, зависящая от даты начала и окончания

#r #dataframe #lubridate

Вопрос:

Я пытаюсь построить для каждого дня, недели и месяца сумму столбцов x. Если конкретный день, неделя или месяц находится между датой начала и датой окончания, я хочу включить число x и сложить их.

Я построил этот образец фрейма данных:

 library(data.table)
library(lubridate)

df <- data.frame(x=c(13,32,37,21,9,43,12,28),
                 start=c('2018-06-12','2019-02-12','2018-12-30','2020-02-05','2019-09-29','2017-05-19','2019-06-13','2020-04-12'), 
                 end=c('2018-09-13','2019-03-19','2020-01-10','2020-03-17','2020-10-10','2020-01-02','2019-07-19','2021-06-01'))

#convert columns as date
df$start <- as.Date(df$start,"%Y-%m-%d")
df$end <- as.Date(df$end,"%Y-%m-%d")
 

Я пытался сделать цикл for за каждый день, чтобы суммировать столбец x за каждый конкретный период, но мне это не удалось.

 #for loop over days
days <- seq(from=as.Date("2017-01-01"), to=as.Date("2021-07-31"), by="days")
for (i in seq_along(days)){
  print(sum(df$x))}
 

Большое вам спасибо за вашу помощь 🙂

Комментарии:

1. Является ли второй ряд ошибкой? начало в 2019 году и конец в 2018 году ?

2. О да, спасибо, что заметили!

Ответ №1:

Вы можете развернуть дату start и end для каждой строки и создать с ее помощью новую строку. Для каждой даты вы можете sum указать x значения. Мы используем complete , чтобы заполнить недостающие даты, если они существуют.

 library(tidyverse)

df %>%
  mutate(dates = map2(start, end, seq, by = 'days')) %>%
  unnest(dates) %>%
  group_by(dates) %>%
  summarise(x = sum(x)) %>%
  complete(dates = seq(min(dates), max(dates), by = 'days'), fill = list(x = 0)) 

#   dates          x
#   <date>     <dbl>
# 1 2017-05-19    43
# 2 2017-05-20    43
# 3 2017-05-21    43
# 4 2017-05-22    43
# 5 2017-05-23    43
# 6 2017-05-24    43
# 7 2017-05-25    43
# 8 2017-05-26    43
# 9 2017-05-27    43
#10 2017-05-28    43
# … with 1,465 more rows
 

Ответ №2:

в течение нескольких дней попробуйте это:

 library(data.table)
library(lubridate)
library(dplyr)

df <- df %>% 
  mutate(start = as.Date(start),
         end = as.Date(end)) %>% ## convert columns as date
  as.data.table() ## convert frame to table


days <- seq(from=as.Date("2017-01-01"), to=as.Date("2021-07-31"), by="days")
total <- 0

for (day in days) {
  total <- total   df[start <= day amp; end >= day, sum(x)]
}


out:
> print(total)
[1] 72784
 

чтобы сохранить результат за каждый день в таблице:

 days <- seq(from=as.Date("2017-01-01"), to=as.Date("2021-07-31"), by="days")
tab_results <- data.table(Date = as.Date(character()), 
                          x = as.integer() )

for (day in days) {
  tab_results <- tab_results %>% add_row(Date = as.Date(day, origin = "1970-01-01"), 
                                         x = df[start <= day amp; end >= day, sum(x)])
}
 

данные:

 df <- data.frame(x=c(13,32,37,21,9,43,12,28),
                 start=c('2018-06-12','2019-02-12','2018-12-30','2020-02-05','2019-09-29','2017-05-19','2019-06-13','2020-04-12'), 
                 end=c('2018-09-13','2018-03-19','2020-01-10','2020-03-17','2020-10-10','2020-01-02','2019-07-19','2021-06-01'))
 

Комментарии:

1. большое спасибо за ваш ответ! Но я пытаюсь получить сумму за каждый отдельный день, а не общую сумму в целом. Так, например, за 2017-05-19 (и последующие дни) сумма составит 43, а за 2018-06-12 (и последующие дни до 2018-09-13) — 56 (43 13) и так далее. Извините, что не уточнил это более точно.