【问题标题】:How to aggregate a dataframe by week?如何按周聚合数据框?
【发布时间】:2017-03-26 01:16:26
【问题描述】:

考虑以下示例

library(tidyverse)
library(lubridate)

time <- seq(from =ymd("2014-02-24"),to= ymd("2014-03-20"), by="days")
set.seed(123)
values <- sample(seq(from = 20, to = 50, by = 5), size = length(time), replace = TRUE)
df2 <- data_frame(time, values)
df2 <- df2 %>% mutate(day_of_week = wday(time, label = TRUE))

Source: local data frame [25 x 3]

         time values day_of_week
       <date>  <dbl>      <fctr>
1  2014-02-24     30         Mon
2  2014-02-25     45        Tues
3  2014-02-26     30         Wed
4  2014-02-27     50       Thurs
5  2014-02-28     50         Fri
6  2014-03-01     20         Sat
7  2014-03-02     35         Sun
8  2014-03-03     50         Mon
9  2014-03-04     35        Tues
10 2014-03-05     35         Wed

我想按周汇总这个数据框。

也就是说,假设我将一周定义为从周一早上开始到周日晚上结束,我们称之为Monday to Monday 循环。 (重要的是,我希望能够选择其他约定,例如周五到周五)。

然后,我只想计算每周values 的平均值。

例如,在上面的示例中,可以计算 2 月 24 日星期一到 3 月 2 日星期日之间values 的平均值,以此类推。

我该怎么做?

【问题讨论】:

  • df2 %&gt;% group_by(week = week(time)) %&gt;% summarise(value = mean(values)),或改用isoweek
  • @Frank,完成,感谢您的评论
  • 如果提到的 week 函数 alistaire 不是您想要的,您可以随时对数据进行排序,然后再进行 cumsum(day_of_week == "Mon")。但是,如果您没有记录每一天,结果将会中断。
  • 哦,是的,你是对的,重复也可能是个麻烦。可以创建一个从 min 到 max date 的辅助表,在那里创建 week var 并合并,但这可能会很麻烦。
  • 还有cut.Date,如果你愿意,可以在周日或周一开始。否则,您可以添加/减去适当的天数并使用任何选项来移动切割点。

标签: r dplyr xts lubridate


【解决方案1】:

在tidyverse中,

df2 %>% group_by(week = week(time)) %>% summarise(value = mean(values))

## # A tibble: 5 × 2
##    week    value
##   <dbl>    <dbl>
## 1     8 37.50000
## 2     9 38.57143
## 3    10 38.57143
## 4    11 36.42857
## 5    12 45.00000

或者改用isoweek

df2 %>% group_by(week = isoweek(time)) %>% summarise(value = mean(values))

## # A tibble: 4 × 2
##    week    value
##   <int>    <dbl>
## 1     9 37.14286
## 2    10 40.71429
## 3    11 35.00000
## 4    12 42.50000

cut.Date:

df2 %>% group_by(week = cut(time, "week")) %>% summarise(value = mean(values))

## # A tibble: 4 × 2
##         week    value
##       <fctr>    <dbl>
## 1 2014-02-24 37.14286
## 2 2014-03-03 40.71429
## 3 2014-03-10 35.00000
## 4 2014-03-17 42.50000

如果您愿意,可以告诉您从周日开始:

df2 %>% group_by(week = cut(time, "week", start.on.monday = FALSE)) %>% 
    summarise(value = mean(values))

## # A tibble: 4 × 2
##         week    value
##       <fctr>    <dbl>
## 1 2014-02-23 37.50000
## 2 2014-03-02 40.00000
## 3 2014-03-09 33.57143
## 4 2014-03-16 44.00000

如果您想改到星期二开始,请在您的日期中添加一个:

df2 %>% group_by(week = cut(time + 1, "week")) %>% summarise(value = mean(values))

## # A tibble: 4 × 2
##         week    value
##       <fctr>    <dbl>
## 1 2014-02-24 37.50000
## 2 2014-03-03 40.00000
## 3 2014-03-10 33.57143
## 4 2014-03-17 44.00000

不过,标签将关闭。如果使用cut,请考虑其include.lowestright 参数的含义,记录在?cut

【讨论】:

    【解决方案2】:

    为什么不直接使用floor_date 和一个整数来调整一周的开始日期?

    library(lubridate)
    time <- seq(from =ymd("2014-02-24"),to= ymd("2014-03-20"), by="days")
    
    set.seed(123)
    
    values <- sample(seq(from = 20, to = 50, by = 5), size = length(time), replace = TRUE)  
    df2 <- data_frame(time, values)
    df2 <- df2 %>% mutate(day_of_week = weekdays(time))
    
    # week wednesday to tuesday
    df2 %>% group_by(Week = floor_date(time-3, unit="week")) %>% 
      summarize(WeeklyAveDist=mean(values), mean(values), min_date = min(time), max_date = max(time)) %>% mutate(weekdays(min_date), weekdays(max_date)))
    
            Week WeeklyAveDist mean.values.   min_date   max_date
    1 2014-02-16      37.50000     37.50000 2014-02-24 2014-02-25
    2 2014-02-23      38.57143     38.57143 2014-02-26 2014-03-04
    3 2014-03-02      38.57143     38.57143 2014-03-05 2014-03-11
    4 2014-03-09      36.42857     36.42857 2014-03-12 2014-03-18
    5 2014-03-16      45.00000     45.00000 2014-03-19 2014-03-20
      weekdays.min_date. weekdays.max_date.
    1             Monday            Tuesday
    2          Wednesday            Tuesday
    3          Wednesday            Tuesday
    4          Wednesday            Tuesday
    5          Wednesday           Thursday
    
    
    # Week Thursday to Wednesday
    df2 %>% group_by(Week = floor_date(time-4, unit="week")) %>% 
      summarize(WeeklyAveDist=mean(values), mean(values), min_date = min(time), max_date = max(time)) %>% mutate(weekdays(min_date), weekdays(max_date)))
    
            Week WeeklyAveDist mean.values.   min_date   max_date
    1 2014-02-16      35.00000     35.00000 2014-02-24 2014-02-26
    2 2014-02-23      39.28571     39.28571 2014-02-27 2014-03-05
    3 2014-03-02      37.14286     37.14286 2014-03-06 2014-03-12
    4 2014-03-09      40.00000     40.00000 2014-03-13 2014-03-19
    5 2014-03-16      40.00000     40.00000 2014-03-20 2014-03-20
      weekdays.min_date. weekdays.max_date.
    1             Monday          Wednesday
    2           Thursday          Wednesday
    3           Thursday          Wednesday
    4           Thursday          Wednesday
    5           Thursday           Thursday
    

    【讨论】:

    • 这可能是最干净的。
    • 只是为了确定,你能解释一下floor_date(time-4, unit="week")的作用吗?
    • 来自文档,第 50 页:“floor_date 采用日期时间对象并将其向下舍入到指定时间单位的最近边界”。 cran.r-project.org/web/packages/lubridate/lubridate.pdf
    • 谢谢!但我不明白如何用时间减去 n 来解决问题
    • 它从聚合到一周之前的日期中减去天的整数值。尝试任何POSIXct / date 类对象减去整数值并查看结果。
    【解决方案3】:
    aggregate(df2$values,by=list(week(df2$time)),mean)
    
      Group.1        x
    1       8 30.00000
    2       9 40.00000
    3      10 36.42857
    4      11 37.85714
    5      12 43.33333
    

    这使用了 lubridate 的week 函数,并给出了一年中的周数。

    要控制一周中的哪一天是开始日,只需参考该主题的此线程:

    Changing lubridate function to start on Monday rather than Sunday

    nograpes 的该线程的解决方案表明,如果您想要一个自定义版本的 week() 函数,使用一周中的任意一天作为一周的开始,您只需像这样从基础 R 构造它:

    start.of.week <- function(date)
      date - (setNames(c(6,0:5),0:6) [strftime(date,'%w')])
    
    end.of.week <- function(date)
      date + (setNames(c(0,6:1),0:6) [strftime(date,'%w')])
    
    start.of.week(as.Date(c('2014-01-05','2014-10-02','2014-09-22','2014-09-27')))
    # "2013-12-30" "2014-09-29" "2014-09-22" "2014-09-22"
    end.of.week(as.Date(c('2014-01-05','2014-10-02','2014-09-22','2014-09-27')))
    # "2014-01-05" "2014-10-05" "2014-09-28" "2014-09-28"
    

    将来lubridate 将在几周内为任意开始日期提供此选项,但 Hadley 还没有时间添加它 (https://github.com/hadley/lubridate/issues/257)。

    【讨论】:

    • 感谢@Hack-R,但您的解决方案不提供对周周期的控制。此外,通过查看组标签不可能知道我们进入了哪一周
    • @Noobie 这是您在问题中要求的周周期。它是一年中一周的周数。您希望如何标记这一周?
    • 好的,谢谢。标签并不重要,因为我总是可以连接年-周数。但是,想象一下我更喜欢周五到周五的周期。那我该如何调整你的解决方案呢?
    • 感谢@Hack-R,超级有用。但是,您介意快速解释一下这里发生了什么吗?我不明白这个函数在做什么......
    • 我的意思是Date - (setNames(c(6,0:5),0:6) [strftime(date,'%w')]) 部分
    【解决方案4】:

    就这一次,经过一些研究,我实际上认为我想出了一个更好的解决方案

    • 提供正确的聚合
    • 给出正确的标签

    以下示例从星期四开始的几周。星期将按给定周期的第一天标记。

    library(tidyverse)
    library(lubridate)
    options(tibble.print_min = 30)
    
    time <- seq(from =ymd("2014-02-24"),to= ymd("2014-03-20"), by="days")
    set.seed(123)
    values <- sample(seq(from = 20, to = 50, by = 5), size = length(time), replace = TRUE)
    df2 <- data_frame(time, values)
    
    df2 <- df2 %>% mutate(day_of_week_label = wday(time, label = TRUE),
                          day_of_week = wday(time, label = FALSE))
    
    df2 <- df2 %>% mutate(thursday_cycle = time - ((as.integer(day_of_week) - 5) %% 7),
                          tmp_1 = (as.integer(day_of_week) - 5),
                          tmp_2 = ((as.integer(day_of_week) - 5) %% 7))
    

    给了

    > df2
    # A tibble: 25 × 7
             time values day_of_week_label day_of_week thursday_cycle tmp_1 tmp_2
           <date>  <dbl>             <ord>       <dbl>         <date> <dbl> <dbl>
    1  2014-02-24     30               Mon           2     2014-02-20    -3     4
    2  2014-02-25     45              Tues           3     2014-02-20    -2     5
    3  2014-02-26     30               Wed           4     2014-02-20    -1     6
    4  2014-02-27     50             Thurs           5     2014-02-27     0     0
    5  2014-02-28     50               Fri           6     2014-02-27     1     1
    6  2014-03-01     20               Sat           7     2014-02-27     2     2
    7  2014-03-02     35               Sun           1     2014-02-27    -4     3
    8  2014-03-03     50               Mon           2     2014-02-27    -3     4
    9  2014-03-04     35              Tues           3     2014-02-27    -2     5
    10 2014-03-05     35               Wed           4     2014-02-27    -1     6
    11 2014-03-06     50             Thurs           5     2014-03-06     0     0
    12 2014-03-07     35               Fri           6     2014-03-06     1     1
    13 2014-03-08     40               Sat           7     2014-03-06     2     2
    14 2014-03-09     40               Sun           1     2014-03-06    -4     3
    15 2014-03-10     20               Mon           2     2014-03-06    -3     4
    16 2014-03-11     50              Tues           3     2014-03-06    -2     5
    17 2014-03-12     25               Wed           4     2014-03-06    -1     6
    18 2014-03-13     20             Thurs           5     2014-03-13     0     0
    19 2014-03-14     30               Fri           6     2014-03-13     1     1
    20 2014-03-15     50               Sat           7     2014-03-13     2     2
    21 2014-03-16     50               Sun           1     2014-03-13    -4     3
    22 2014-03-17     40               Mon           2     2014-03-13    -3     4
    23 2014-03-18     40              Tues           3     2014-03-13    -2     5
    24 2014-03-19     50               Wed           4     2014-03-13    -1     6
    25 2014-03-20     40             Thurs           5     2014-03-20     0     0
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-11
      相关资源
      最近更新 更多