【问题标题】:Data.table solution to calculate weekly average up to varying total number of weeks if date falls within a date interval如果日期在日期间隔内,Data.table 解决方案可计算每周平均值,最多可改变总周数
【发布时间】:2020-12-26 09:30:48
【问题描述】:

我每天都会测量环境数据。对于每个 ID,如果每日度量在该周内(包括 Date_start),我想为该特定 ID 创建每周平均值,最多为该特定 ID 的总周数。每个 ID 的最后一周将有不同的长度。每个周平均值将是该 ID 的一个新列。

一些数据:

   O3   Date       ID Date_start Date_end  Total_weeks 
 1 21.1 1980-01-28 a 1980-01-22 1980-02-08  2.428571       
 2 27.3 1980-01-30 a 1980-01-22 1980-02-08  2.428571       
 3 23.8 1980-01-31 a 1980-01-22 1980-02-08  2.428571       
 4 29.5 1980-02-01 a 1980-01-22 1980-02-08  2.428571   
 5 23.8 1980-01-29 a 1980-01-22 1980-02-08  2.428571      
 6 27.1 1980-02-03 a 1980-01-22 1980-02-08  2.428571     
 7 31.6 1980-02-04 a 1980-01-22 1980-02-08  2.428571    
 8 25.8 1980-02-05 a 1980-01-22 1980-02-08  2.428571    
 9 31.2 1980-02-02 a 1980-01-22 1980-02-08  2.428571   
10 14.0 1980-02-07 a 1980-01-22 1980-02-08  2.428571  
11 19.1 1980-01-22 a 1980-01-22 1980-02-08  2.428571 
12 15.5 1980-01-24 a 1980-01-22 1980-02-08  2.428571  
 
13 15.6 1980-01-26 b 1980-01-26 1980-02-14. 2.714286
14 28.6 1980-01-27 b 1980-01-26 1980-02-14  2.714286
15 16.9 1980-02-04 b 1980-01-26 1980-02-14  2.714286
16 27.4 1980-02-05 b 1980-01-26 1980-02-14  2.714286
17 30.1 1980-02-06 b 1980-01-26 1980-02-14  2.714286
18 24.4 1980-02-10 b 1980-01-26 1980-02-14  2.714286
19 21.2 1980-01-30 b 1980-01-26 1980-02-14  2.714286
20 22.1 1980-02-11 b 1980-01-26 1980-02-14  2.714286
21 26.1 1980-02-13 b 1980-01-26 1980-02-14  2.714286
21 19.9 1980-02-14 b 1980-01-26 1980-02-14  2.714286


例如,ID“a”将具有以下每周平均值:

Week 1: mean(O3) for 1980-01-22 <= Date < 1980-01-29
Week 2: mean(O3) for 1980-01-29 <= Date < 1980-02-05
Week 3: mean(O3) for 1980-02-05 <= Date < 1980-02-08

第 3 周不是整周,而 1980-02-08 是 Date_end。

我正在尝试编写一个嵌套循环,该循环遍历数据框中的每个唯一 ID,根据 Total_weeks 计算一系列数字,然后遍历该序列以计算每周平均值。

我在想什么:


for (i in unique(ID)) {

  sequence[i] <- seq(from = 1, to = unique(Total_weeks[i]), by = 7) 
  
  for (ii in 1:length(sequence[i])) {
    
    week[ii] <- mean(O3[Date >= Date_start + first_number_in_sequence & Date < Date_start + next_number_in_sequence[i]])
    
  }
    
}

但我不确定如何编写代码,以便根据序列中的正确数字计算每周平均值。我还尝试在 dplyr 中执行此操作,以便可以按 ID 分组,但不确定如何为每个唯一 ID 创建不同数量的列(因为它们有不同的 Total_weeks)。

任何见解将不胜感激。

【问题讨论】:

  • 使用week 来自lubridate 来查找星期nr。然后使用dplyr 中的group_by 按周对数据进行分组。任何以下计算(例如汇总到平均值)都将应用于您的分组。另外,请使用dput(head(data, 25)) 在此处发布您的数据,以便我们实际运行您的代码。
  • 第nr周是什么意思?另外,您能澄清一下 dput(head(data, 25)) 的用途吗?不确定我需要在帖子中添加什么。 @PaulvanOppen
  • dput 以您需要帮助的用户可以轻松地重新创建您的数据对象的方式返回数据对象。由于我们没有注意您的所有数据,head 函数返回数据对象的前 n 行。 lubridateweek 函数从数据对象返回第 nr 周。

标签: r loops date dplyr


【解决方案1】:

使用tidyverse,您可以尝试以下操作。

首先group_by(ID),然后根据开始日期确定每行日期的周数。

然后,group_by ID 和周数并计算每周的平均值 O3。如果有缺失数据,可以考虑na.rm = TRUE

最后,如果您希望每周都有列,并且每个 ID 有一行,请考虑使用 pivot_wider

请注意,这会忽略 Date_end(假设您对没有数据的周不感兴趣)。

library(tidyverse)

df %>%
  group_by(ID) %>%
  mutate(week = floor(difftime(Date, Date_start, units = "weeks")) + 1) %>%
  group_by(ID, week) %>%
  summarise(weekly_mean = mean(O3)) %>%
  pivot_wider(id_cols = ID, names_from = "week", values_from = "weekly_mean", names_prefix = "week_")

我相信 data.table 等价物可能是这样的:

library(data.table)

setDT(df)

dcast(df[, week := floor(difftime(Date, Date_start, units = "weeks")) + 1, by = .(ID)][,
  .(weekly_mean = mean(O3)), by = .(ID, week)], ID ~ paste("week", week, sep = "_"), value.var = "weekly_mean")

输出

  ID    week_1 week_2 week_3
  <chr>  <dbl>  <dbl>  <dbl>
1 a       18.6   27.8   19.9
2 b       21.8   24.8   23.1

数据

df <- structure(list(O3 = c(21.1, 27.3, 23.8, 29.5, 23.8, 27.1, 31.6, 
25.8, 31.2, 14, 19.1, 15.5, 15.6, 28.6, 16.9, 27.4, 30.1, 24.4, 
21.2, 22.1, 26.1, 19.9), Date = structure(c(3679, 3681, 3682, 
3683, 3680, 3685, 3686, 3687, 3684, 3689, 3673, 3675, 3677, 3678, 
3686, 3687, 3688, 3692, 3681, 3693, 3695, 3696), class = "Date"), 
    ID = c("a", "a", "a", "a", "a", "a", "a", "a", "a", "a", 
    "a", "a", "b", "b", "b", "b", "b", "b", "b", "b", "b", "b"
    ), Date_start = structure(c(3673, 3673, 3673, 3673, 3673, 
    3673, 3673, 3673, 3673, 3673, 3673, 3673, 3677, 3677, 3677, 
    3677, 3677, 3677, 3677, 3677, 3677, 3677), class = "Date"), 
    Date_end = structure(c(3690, 3690, 3690, 3690, 3690, 3690, 
    3690, 3690, 3690, 3690, 3690, 3690, 3696, 3696, 3696, 3696, 
    3696, 3696, 3696, 3696, 3696, 3696), class = "Date"), Total_weeks = c(2.428571, 
    2.428571, 2.428571, 2.428571, 2.428571, 2.428571, 2.428571, 
    2.428571, 2.428571, 2.428571, 2.428571, 2.428571, 2.714286, 
    2.714286, 2.714286, 2.714286, 2.714286, 2.714286, 2.714286, 
    2.714286, 2.714286, 2.714286)), row.names = c(NA, -22L), class = "data.frame")

【讨论】:

  • 谢谢 - 这适用于我拥有的数据的一个子集,但我认为我必须将我的完整数据集作为 data.table 使用。我将如何使用 data.table 解决方案来解决这个问题?
  • @user12310746 请使用data.table 方法查看编辑后的答案。请让我知道这是否适合您。
  • 抱歉延迟回复。我今天试了一下,效果很好。
  • 添加到上面的解决方案 - 如果我想计算累积的每周平均值(例如过去 2 周),我是否必须再次用 dcast 包装现有代码以创建列名累积平均值?
猜你喜欢
  • 1970-01-01
  • 2022-12-06
  • 2015-10-17
  • 2018-01-24
  • 1970-01-01
  • 1970-01-01
  • 2013-02-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多