【问题标题】:How to calculate the mean of the previous 5 values?如何计算前 5 个值的平均值?
【发布时间】:2020-08-21 11:25:54
【问题描述】:

我有以下数据框:

agrodata

    week             temperature        humidity       radiation    evapotranspiration
   <date>                  <dbl>            <dbl>            <dbl>            <dbl>
2012-03-25               15.9             54.1            20.4            0     
2012-06-17               25.9             65.6            22.2            0.486 
2012-06-24               27.4             61.7            21.3            0     
2012-07-08               27.5             62.9            22.4            0     
2012-07-15               27.5             50.1            23.1            0     
2012-07-22               27.0             56.4            19.0            0     
2012-07-29               28.7             61.5            19.7            0     
2012-08-05               29.1             56.1            20.0            0.0286
2012-08-12               28.6             56.8            19.4            0     
2012-08-19               29.0             63.0            18.1            0     
2012-08-26               27.4             62.9            18.0            0.229 
2012-09-02               16.9             32.9            16.9            0     
2012-09-09               25.0             62.1            16.7            0     
2012-09-16               25.4             62.6            14.5            0     
2012-09-30               22.3             65.6            15.2            0     
2012-10-07               22.4             71.4            13.9            0     
2012-10-14               19.3             67.2            11.3            0.257 
2012-10-21               18.7             74.5            9.70           3.43  
2012-10-28               16.4             75.5            8.35           3.2   
2012-11-04               17.8             79.7            7.17           7

如您所见,我将日期按周分组。我想创建 4 个新列(对于 4 个气候变量中的每一个),其中包含前连续 5 周的平均值,如果没有前连续 5 周,那么无论如何......有什么办法吗它与 dplyr?谢谢。

【问题讨论】:

  • 这能回答你的问题吗? Consecutive/Rolling sums in a vector in R
  • 缺少周数时会发生什么?我们是否需要从当前周到 5 周前的一周之间的所有周?还是仅在过去 5 周的最后一周与当前周之间的缺失?还是您的意思是前 5 行,无论它们是否在 5 周内?本周是包括在平均值中还是严格意义上的前一周?
  • 我们在前 5 周里占用的时间一样多。我们需要从当前周到 5 周前的一周之间的所有周。如果前一周没有 5 个,我们可以取 4 个,或 3 个,或 2 个,甚至 1 个(当前周)。包括实际周数,因此我们需要 6 个值的平均值(前 5 周和当前周)。谢谢!

标签: r dplyr


【解决方案1】:

我建议使用zoo 中的rollmean() 和dplyr 中的across() 的tidyverse 方法:

library(tidyverse)
library(zoo)
#Data
df <- structure(list(week = c("25/03/2012", "17/06/2012", "24/06/2012", 
"08/07/2012", "15/07/2012", "22/07/2012", "29/07/2012", "05/08/2012", 
"12/08/2012", "19/08/2012", "26/08/2012", "02/09/2012", "09/09/2012", 
"16/09/2012", "30/09/2012", "07/10/2012", "14/10/2012", "21/10/2012", 
"28/10/2012", "04/11/2012"), temperature = c(15.9, 25.9, 27.4, 
27.5, 27.5, 27, 28.7, 29.1, 28.6, 29, 27.4, 16.9, 25, 25.4, 22.3, 
22.4, 19.3, 18.7, 16.4, 17.8), humidity = c(54.1, 65.6, 61.7, 
62.9, 50.1, 56.4, 61.5, 56.1, 56.8, 63, 62.9, 32.9, 62.1, 62.6, 
65.6, 71.4, 67.2, 74.5, 75.5, 79.7), radiation = c(20.4, 22.2, 
21.3, 22.4, 23.1, 19, 19.7, 20, 19.4, 18.1, 18, 16.9, 16.7, 14.5, 
15.2, 13.9, 11.3, 9.7, 8.35, 7.17), evapotranspiration = c(0, 
0.486, 0, 0, 0, 0, 0, 0.0286, 0, 0, 0.229, 0, 0, 0, 0, 0, 0.257, 
3.43, 3.2, 7)), class = "data.frame", row.names = c(NA, -20L))

代码:

df %>%
  mutate(across(c(temperature:evapotranspiration),
                .fns = list(avg = ~ rollmean(.,k=5,fill=NA,align = 'right')))) -> df2

输出:

         week temperature humidity radiation evapotranspiration temperature_avg humidity_avg radiation_avg
1  25/03/2012        15.9     54.1     20.40             0.0000              NA           NA            NA
2  17/06/2012        25.9     65.6     22.20             0.4860              NA           NA            NA
3  24/06/2012        27.4     61.7     21.30             0.0000              NA           NA            NA
4  08/07/2012        27.5     62.9     22.40             0.0000              NA           NA            NA
5  15/07/2012        27.5     50.1     23.10             0.0000           24.84        58.88        21.880
6  22/07/2012        27.0     56.4     19.00             0.0000           27.06        59.34        21.600
7  29/07/2012        28.7     61.5     19.70             0.0000           27.62        58.52        21.100
8  05/08/2012        29.1     56.1     20.00             0.0286           27.96        57.40        20.840
9  12/08/2012        28.6     56.8     19.40             0.0000           28.18        56.18        20.240
10 19/08/2012        29.0     63.0     18.10             0.0000           28.48        58.76        19.240
11 26/08/2012        27.4     62.9     18.00             0.2290           28.56        60.06        19.040
12 02/09/2012        16.9     32.9     16.90             0.0000           26.20        54.34        18.480
13 09/09/2012        25.0     62.1     16.70             0.0000           25.38        55.54        17.820
14 16/09/2012        25.4     62.6     14.50             0.0000           24.74        56.70        16.840
15 30/09/2012        22.3     65.6     15.20             0.0000           23.40        57.22        16.260
16 07/10/2012        22.4     71.4     13.90             0.0000           22.40        58.92        15.440
17 14/10/2012        19.3     67.2     11.30             0.2570           22.88        65.78        14.320
18 21/10/2012        18.7     74.5      9.70             3.4300           21.62        68.26        12.920
19 28/10/2012        16.4     75.5      8.35             3.2000           19.82        70.84        11.690
20 04/11/2012        17.8     79.7      7.17             7.0000           18.92        73.66        10.084
   evapotranspiration_avg
1                      NA
2                      NA
3                      NA
4                      NA
5                 0.09720
6                 0.09720
7                 0.00000
8                 0.00572
9                 0.00572
10                0.00572
11                0.05152
12                0.05152
13                0.04580
14                0.04580
15                0.04580
16                0.00000
17                0.05140
18                0.73740
19                1.37740
20                2.77740

【讨论】:

  • 可以设置partial = TRUE 取部分组的平均值。
  • 您好,非常感谢您的回答。这是一个很好的近似值,但并不完全是我需要的。我只是在帖子中评论了一些细节,你能看一下吗?
【解决方案2】:

1) 滚动应用

使用此答案末尾注释中显示的agroweek,我们假设:

  • 问题中的数据具有代表性,即按周排序,没有NA。 (放宽这些假设并不难,并且在任何情况下 (2) 都允许 NA 和无序数据。)
  • 需要的是过去 6 周内所有周的平均值(而不是最后 6 行)
  • 过去 6 周包括当前周

使用findInterval 找到 6 周前的那一周的行号,然后从当前行号中减去该行号,得到 width,该向量包含每个点的平均行数。然后使用rollapplyr 计算r 滚动方式,将mean_ 添加到列名,cbind 将它们添加到原始数据框。另一个示例,请参阅?rollapply 的示例部分中涉及findInterval 的示例。

library(zoo)

k <- 6 # no of weeks
week <- agroweek$week
width <- seq_along(week) - findInterval(week - 7*k, week)
r <- rollapplyr(agroweek[-1], width, mean)
colnames(r) <- paste("mean", colnames(r), sep = "_")
res1 <- cbind(agroweek, r)

给予:

> head(res1)
        week temperature humidity radiation evapotranspiration mean_temperature mean_humidity mean_radiation mean_evapotranspiration
1 2012-03-25        15.9     54.1      20.4              0.000         15.90000        54.100       20.40000                  0.0000
2 2012-06-17        25.9     65.6      22.2              0.486         25.90000        65.600       22.20000                  0.4860
3 2012-06-24        27.4     61.7      21.3              0.000         26.65000        63.650       21.75000                  0.2430
4 2012-07-08        27.5     62.9      22.4              0.000         26.93333        63.400       21.96667                  0.1620
5 2012-07-15        27.5     50.1      23.1              0.000         27.07500        60.075       22.25000                  0.1215
6 2012-07-22        27.0     56.4      19.0              0.000         27.06000        59.340       21.60000                  0.0972

2) SQL

另一种方法是在 SQL 中按周进行左自连接,并对每一行的连接值进行平均。即使有 NA 并且即使数据未排序,这也有效。

library(sqldf)

k <- 6
fn$sqldf("select a.*, 
    avg(b.temperature) as mean_temperature,
    avg(b.humidity) as mean_humidity,
    avg(b.radiation) as mean_radiation,
    avg(b.evapotranspiration) as mean_evapotranspiration 
  from agroweek as a
  left join agroweek as b on b.week between a.week - ($k-1)*7 and a.week
  group by a.week")

这也可以写成:

k <- 6
means <- toString(sprintf("avg(b.%s) as mean_%s", names(DF)[-1], names(DF)[-1]))
fn$sqldf("select a.*, $means
  from agroweek as a
  left join agroweek as b on b.week between a.week - ($k-1)*7 and a.week
  group by a.week")

注意

假设可重现形式的输入为:

agroweek <- structure(list(week = structure(c(15424, 15508, 15515, 15529, 
15536, 15543, 15550, 15557, 15564, 15571, 15578, 15585, 15592, 
15599, 15613, 15620, 15627, 15634, 15641, 15648), class = "Date"), 
    temperature = c(15.9, 25.9, 27.4, 27.5, 27.5, 27, 28.7, 29.1, 
    28.6, 29, 27.4, 16.9, 25, 25.4, 22.3, 22.4, 19.3, 18.7, 16.4, 
    17.8), humidity = c(54.1, 65.6, 61.7, 62.9, 50.1, 56.4, 61.5, 
    56.1, 56.8, 63, 62.9, 32.9, 62.1, 62.6, 65.6, 71.4, 67.2, 
    74.5, 75.5, 79.7), radiation = c(20.4, 22.2, 21.3, 22.4, 
    23.1, 19, 19.7, 20, 19.4, 18.1, 18, 16.9, 16.7, 14.5, 15.2, 
    13.9, 11.3, 9.7, 8.35, 7.17), evapotranspiration = c(0, 0.486, 
    0, 0, 0, 0, 0, 0.0286, 0, 0, 0.229, 0, 0, 0, 0, 0, 0.257, 
    3.43, 3.2, 7)), row.names = c(NA, -20L), class = "data.frame")

可以这样生成:

Lines <- "
     week             temperature        humidity       radiation    evapotranspiration
2012-03-25               15.9             54.1            20.4            0     
2012-06-17               25.9             65.6            22.2            0.486 
2012-06-24               27.4             61.7            21.3            0     
2012-07-08               27.5             62.9            22.4            0     
2012-07-15               27.5             50.1            23.1            0     
2012-07-22               27.0             56.4            19.0            0     
2012-07-29               28.7             61.5            19.7            0     
2012-08-05               29.1             56.1            20.0            0.0286
2012-08-12               28.6             56.8            19.4            0     
2012-08-19               29.0             63.0            18.1            0     
2012-08-26               27.4             62.9            18.0            0.229 
2012-09-02               16.9             32.9            16.9            0     
2012-09-09               25.0             62.1            16.7            0     
2012-09-16               25.4             62.6            14.5            0     
2012-09-30               22.3             65.6            15.2            0     
2012-10-07               22.4             71.4            13.9            0     
2012-10-14               19.3             67.2            11.3            0.257 
2012-10-21               18.7             74.5            9.70           3.43  
2012-10-28               16.4             75.5            8.35           3.2   
2012-11-04               17.8             79.7            7.17           7"

agroweek <- read.table(text = Lines, header = TRUE)
agroweek$week <- as.Date(agroweek$week)

【讨论】:

  • 您好,感谢您的回答。我有一个问题,你是如何转换日期的?
  • 见末尾的扩展注释。将来请以可复制的形式提供输入。
  • 当我尝试 width &lt;- seq_along(week) - findInterval(week - 7*k, week) 时,我收到此错误:findInterval 中的错误(周 - 7 * 6,周):'vec' 必须以非递减方式排序并且不包含 NAs
  • 启动一个新的 R 实例,然后复制 Note 中的代码并粘贴进去,然后复制 (1) 中的代码并粘贴进去。现在显示了输出的前 6 行在答案中,您可以看到没有错误。如果您有 NA 或未对周进行排序,则 (1) 使用 na.omit 省略具有 NA 的行并对数据进行排序。请注意,(2) 甚至适用于 NA,即使数据未排序。
【解决方案3】:

使用data.table:

setDT(df)
cols <- names(df)[-1]
df[, paste0(cols, "_5w_avg") := 
       lapply(.SD, function(x) fcoalesce(frollmean(x, n=5:1, na.rm=TRUE))), 
   .SDcols = cols]
df
#           week temperature humidity radiation evapotranspiration temperature_5w_avg humidity_5w_avg radiation_5w_avg evapotranspiration_5w_avg
#  1: 25/03/2012        15.9     54.1     20.40             0.0000           15.90000        54.10000           20.400              0.000000e+00
#  2: 17/06/2012        25.9     65.6     22.20             0.4860           20.90000        59.85000           21.300              2.430000e-01
#  3: 24/06/2012        27.4     61.7     21.30             0.0000           23.06667        60.46667           21.300              1.620000e-01
#  4: 08/07/2012        27.5     62.9     22.40             0.0000           24.17500        61.07500           21.575              1.215000e-01
#  5: 15/07/2012        27.5     50.1     23.10             0.0000           24.84000        58.88000           21.880              9.720000e-02
#  6: 22/07/2012        27.0     56.4     19.00             0.0000           27.06000        59.34000           21.600              9.720000e-02
#  7: 29/07/2012        28.7     61.5     19.70             0.0000           27.62000        58.52000           21.100              0.000000e+00
#  8: 05/08/2012        29.1     56.1     20.00             0.0286           27.96000        57.40000           20.840              5.720000e-03
#  9: 12/08/2012        28.6     56.8     19.40             0.0000           28.18000        56.18000           20.240              5.720000e-03
# 10: 19/08/2012        29.0     63.0     18.10             0.0000           28.48000        58.76000           19.240              5.720000e-03
# 11: 26/08/2012        27.4     62.9     18.00             0.2290           28.56000        60.06000           19.040              5.152000e-02
# 12: 02/09/2012        16.9     32.9     16.90             0.0000           26.20000        54.34000           18.480              5.152000e-02
# 13: 09/09/2012        25.0     62.1     16.70             0.0000           25.38000        55.54000           17.820              4.580000e-02
# 14: 16/09/2012        25.4     62.6     14.50             0.0000           24.74000        56.70000           16.840              4.580000e-02
# 15: 30/09/2012        22.3     65.6     15.20             0.0000           23.40000        57.22000           16.260              4.580000e-02
# 16: 07/10/2012        22.4     71.4     13.90             0.0000           22.40000        58.92000           15.440             -5.551115e-18
# 17: 14/10/2012        19.3     67.2     11.30             0.2570           22.88000        65.78000           14.320              5.140000e-02
# 18: 21/10/2012        18.7     74.5      9.70             3.4300           21.62000        68.26000           12.920              7.374000e-01
# 19: 28/10/2012        16.4     75.5      8.35             3.2000           19.82000        70.84000           11.690              1.377400e+00
# 20: 04/11/2012        17.8     79.7      7.17             7.0000           18.92000        73.66000           10.084              2.777400e+00

【讨论】:

    猜你喜欢
    • 2017-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-07
    相关资源
    最近更新 更多