【问题标题】:Aggregation by time period in lubridatelubridate 中按时间段聚合
【发布时间】:2013-08-04 18:31:07
【问题描述】:

This question 询问 R 中按时间段聚合的问题,pandas 称之为重采样。最有用的答案是使用 XTS 包按给定时间段分组,应用一些函数,例如 sum() 或 mean()。

其中一位 cmets 表示在 lubridate 中有类似的东西,但没有详细说明。有人可以提供一个使用 lubridate 的惯用例子吗?我已经通读了几次 lubridate 小插图,并且可以想象 lubridate 和 plyr 的某种组合,但是我想确保没有更简单的方法我错过了。

为了使示例更加真实,假设我想要从此数据集中向北行驶的每日自行车总数:

library(lubridate)
library(reshape2)

bikecounts <- read.csv(url("http://data.seattle.gov/api/views/65db-xm6k/rows.csv?accessType=DOWNLOAD"), header=TRUE, stringsAsFactors=FALSE)
names(bikecounts) <- c("Date", "Northbound", "Southbound")

数据如下所示:

> head(bikecounts)
                    Date Northbound Southbound
1 10/02/2012 12:00:00 AM          0          0
2 10/02/2012 01:00:00 AM          0          0
3 10/02/2012 02:00:00 AM          0          0
4 10/02/2012 03:00:00 AM          0          0
5 10/02/2012 04:00:00 AM          0          0
6 10/02/2012 05:00:00 AM          0          0

【问题讨论】:

    标签: r lubridate


    【解决方案1】:

    我不知道您为什么要为此使用 lubridate。如果你只是在寻找不如 xts 更棒的东西,你可以试试这个

    tapply(bikecounts$Northbound, as.Date(bikecounts$Date, format="%m/%d/%Y"), sum)
    

    基本上,你只需要按日期split,然后应用一个函数。


    lubridate 可用于为拆分应用问题创建分组因子。因此,例如,如果您想要每个月的总和(忽略年份)

    tapply(bikecounts$Northbound, month(mdy_hms(bikecounts$Date)), sum)
    

    但是,它只是对基本 R 函数使用包装器,在 OP 的情况下,我认为基本 R 函数 as.Date 是最简单的(其他答案也忽略了您的使用请求这一事实证明了这一点润滑 ;-) )。


    Answer 与 OP 中链接到的另一个 Question 未涵盖的内容是 split.xtsperiod.applyendpoints 处拆分 xts 并将函数应用于每个组。您可以使用endpoints 函数找到对给定任务有用的端点。例如,如果您有一个 xts 对象 x,那么 endpoints(x, "months") 将为您提供每个月最后一行的行号。 split.xts 利用它来拆分 xts 对象 -- split(x, "months") 将返回 xts 对象列表,其中每个组件用于不同的月份。

    虽然split.xts()endpoints() 主要用于xts 对象,但它们也适用于其他一些对象,包括基于纯时间的向量。即使您不想使用 xts 对象,您仍然可以找到 endpoints() 的用途,因为它方便或速度快(用 C 实现)

    > split.xts(as.Date("1970-01-01") + 1:10, "weeks")
    [[1]]
    [1] "1970-01-02" "1970-01-03" "1970-01-04"
    
    [[2]]
    [1] "1970-01-05" "1970-01-06" "1970-01-07" "1970-01-08" "1970-01-09"
    [6] "1970-01-10" "1970-01-11"
    
    > endpoints(as.Date("1970-01-01") + 1:10, "weeks")
    [1]  0  3 10
    

    我认为 lubridate 在这个问题中的最佳用途是将“日期”字符串解析为 POSIXct 对象。即本例中的 mdy_hms 函数。

    这是一个使用lubridate 解析“日期”字符串的xts 解决方案。

    x <- xts(bikecounts[, -1], mdy_hms(bikecounts$Date))
    period.apply(x, endpoints(x, "days"), sum)
    apply.daily(x, sum) # identical to above
    

    对于这个特定的任务,xts 也有一个优化的 period.sum 函数(用 Fortran 编写),速度非常快

    period.sum(x, endpoints(x, "days"))
    

    【讨论】:

    • 这也很有用。如果共识是“不要使用 lubridate,而是使用 XTS”,我们很乐意接受这个答案。也就是说,我听到很多“使用 lubridate”,所以我有点想知道大惊小怪是怎么回事!
    • 我倾向于同意@GSee。我认为仅使用lubridate 函数实现ddply-able 分组变量的最简单lubridate 方法是:bikecounts$date2 &lt;- mdy_hms(x = bikecounts$Date)bikecounts$date3 &lt;- round_date(x = bc$date2, unit = "day").
    • 每个人的答案都很棒。对此的支持 1.) 纠正我的起始前提 2.) 提供基础 R 示例 3.) 显示 lubridate 版本的外观,以及 4.) 解释 xts 的好处。
    【解决方案2】:

    使用 plyr 包中的ddply

    library(plyr)
    bikecounts$Date<-with(bikecounts,as.Date(Date, format = "%m/%d/%Y"))
    x<-ddply(bikecounts,.(Date),summarise, sumnorth=sum(Northbound),sumsouth=sum(Southbound))
    
    
     > head(x)
            Date sumnorth sumsouth
    1 2012-10-02     1165      773
    2 2012-10-03     1761     1760
    3 2012-10-04     1767     1708
    4 2012-10-05     1590     1558
    5 2012-10-06      926     1080
    6 2012-10-07      951     1191
    
    
     > tail(x)
              Date sumnorth sumsouth
    298 2013-07-26     1964     1999
    299 2013-07-27     1212     1289
    300 2013-07-28      902     1078
    301 2013-07-29     2040     2048
    302 2013-07-30     2314     2226
    303 2013-07-31     2008     2076
    

    【讨论】:

      【解决方案3】:

      这是一个使用data.table 的选项 导入 csv 后:

      library(data.table)
      
      # convert the data.frame to data.table
      bikecounts <- data.table(bikecounts)
      
      # Calculate
      bikecounts[, list(NB=sum(Northbound), SB=sum(Southbound)), by=as.Date(Date, format="%m/%d/%Y")]
      
              as.Date   NB   SB
        1: 2012-10-02 1165  773
        2: 2012-10-03 1761 1760
        3: 2012-10-04 1767 1708
        4: 2012-10-05 1590 1558
        5: 2012-10-06  926 1080
       ---                     
      299: 2013-07-27 1212 1289
      300: 2013-07-28  902 1078
      301: 2013-07-29 2040 2048
      302: 2013-07-30 2314 2226
      303: 2013-07-31 2008 2076
      

      注意,您还可以使用 data.table 包中的fread()(“快速读取”)一步将 CSV 读入 data.table。 唯一的缺点是您手动从字符串转换日期/时间。

      eg: 
      bikecounts <- fread("http://data.seattle.gov/api/views/65db-xm6k/rows.csv?accessType=DOWNLOAD", header=TRUE, stringsAsFactors=FALSE)
      setnames(bikecounts, c("Date", "Northbound", "Southbound"))
      bikecounts[, Date := as.POSIXct(D, format="%m/%d/%Y %I:%M:%S %p")] 
      

      【讨论】:

      • 这个答案在需要真正快速聚合的地方也很好。如果不进行基准测试,我敢打赌这种方法是最快的。
      • 将 data.table 与 lubridate 或 fasttime 混合怎么样?
      【解决方案4】:

      这是请求的 lubridate 解决方案,我也将其添加到链接问题中。它结合使用 lubridatezoo aggregate() 进行这些操作:

      ts.month.sum <- aggregate(zoo.ts, month, sum)
      
      ts.daily.mean <- aggregate(zoo.ts, day, mean)
      
      ts.mins.mean <- aggregate(zoo.ts, minutes, mean)
      

      显然,您需要先将数据转换为 zoo() 对象,这很容易。您还可以使用 yearmon() 或 yearqtr(),或 both 拆分和应用的自定义函数。这种方法在语法上和 pandas 一样好。

      【讨论】:

        猜你喜欢
        • 2012-02-09
        • 2021-01-15
        • 1970-01-01
        • 1970-01-01
        • 2022-11-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多