将不规则时间序列拆分为常规月平均值 - R.

时间:2012-09-24 01:22:38

标签: sql r data.table xts zoo

为了对能源使用产生季节性影响,我需要将计费数据库中的能源使用信息与月度温度保持一致。

我正在使用具有不同长度和开始日期和结束日期的帐单的结算数据集,并且我想获得每个月内每个帐户的月平均值。例如,我有一个具有以下特征的计费数据库:

   acct amount      begin        end days
1  2242  11349 2009-10-06 2009-11-04   29
2  2242  12252 2009-11-04 2009-12-04   30
3  2242  21774 2009-12-04 2010-01-08   35
4  2242  18293 2010-01-08 2010-02-05   28
5  2243  27217 2009-10-06 2009-11-04   29
6  2243    117 2009-11-04 2009-12-04   30
7  2243  14543 2009-12-04 2010-01-08   35

我想弄清楚如何强制这些有些不规则的时间序列(针对每个帐户),以获得每个帐单中每个月内的平均每日金额,以便:

   acct amount      begin        end days avgamtpday
1  2242  11349 2009-10-01 2009-10-31   31          X
2  2242  12252 2009-11-01 2009-11-30   30          X
3  2242  21774 2009-12-01 2010-12-31   31          X
4  2242  18293 2010-01-01 2010-01-31   31          X
4  2242  18293 2010-02-01 2010-02-28   28          X
5  2243  27217 2009-10-01 2009-10-31   31          X
6  2243    117 2009-11-01 2009-11-30   30          X
7  2243  14543 2009-12-01 2009-12-31   30          X
7  2243  14543 2010-01-01 2010-01-31   31          X

我完全不知道哪种工具可以做到这一点,因为我只需要这样做一次。

额外的皱纹是表格大约150,000行,这在大多数标准下并不是非常大,但是足够大以使R中的循环解决方案变得困难。我已经调查过使用R中的zoo,xts和tempdisagg软件包。我开始编写一个非常丑陋的循环来分割每个帐单,然后在现有帐单中每个月创建一行,然后按应用程序汇总tapply()几个月,但老实说,看不出如何有效地做到这一点。

在MySQL中,我试过这个:

  

创建或替换视图v3为select 1 n union all select 1 union all all 1;
  创建或替换视图v作为选择1 n从v3 a,v3 b union all选择1;
  设置@n = 0;
  删除表如果存在日历;   创建表日历(dt日期主键);
  插入日历
     选择演员表('2008-1-1'+ interval @n:= @ n + 1天作为日期)作为dt      来自v a,v b,v c,v d,v e,v;

     

选择acct,amount,begin,end,billAmtPerDay,sum(billAmtPerDay),MonthAmt,   count()天数,总和(billAmtPerDay)/ count()AverageAmtPerDay,year(dt),month(dt)      FROM(选择*,金额/天billAmtPerDay         来自账单b            内部联接日历c在开始和结束之间的dt并开始<> dt)x   按帐户,金额,开始,结束,billAmtPerDay,年(dt),月(dt)分组;

但是由于我不理解的原因,我的服务器不喜欢这个表,并且挂起内部联接,即使我进行不同的计算。我正在调查是否有任何临时内存限制。

谢谢!

2 个答案:

答案 0 :(得分:8)

以下是使用data.table

的开始
billdata <- read.table(text=" acct amount begin end days
1 2242 11349 2009-10-06 2009-11-04 29
2 2242 12252 2009-11-04 2009-12-04 30
3 2242 21774 2009-12-04 2010-01-08 35
4 2242 18293 2010-01-08 2010-02-05 28
5 2243 27217 2009-10-06 2009-11-04 29
6 2243 117 2009-11-04 2009-12-04 30
7 2243 14543 2009-12-04 2010-01-08 35", sep=" ", header=TRUE, row.names=1)

require(data.table)
DT = as.data.table(billdata)

首先,将列beginend的类型更改为日期。与data.frame不同,它不会复制整个数据集。

DT[,begin:=as.Date(begin)]
DT[,end:=as.Date(end)]

然后查找时间跨度,查找每天的现行帐单,并汇总。

alldays = DT[,seq(min(begin),max(end),by="day")]

setkey(DT, acct, begin)

DT[CJ(unique(acct),alldays),
   mean(amount/days,na.rm=TRUE),
   by=list(acct,month=format(begin,"%Y-%m")), roll=TRUE]

    acct   month        V1
 1: 2242 2009-10 391.34483
 2: 2242 2009-11 406.69448
 3: 2242 2009-12 601.43226
 4: 2242 2010-01 646.27465
 5: 2242 2010-02 653.32143
 6: 2243 2009-10 938.51724
 7: 2243 2009-11  97.36172
 8: 2243 2009-12 375.68065
 9: 2243 2010-01 415.51429
10: 2243 2010-02 415.51429

我认为你会发现普遍存在的连接逻辑在SQL中非常麻烦,而且速度较慢。

我说这是一个提示,因为它不太正确。注意第10行是重复的,因为帐户2243不像帐户2242那样延伸到2010-02。要完成它,您可以在每个帐户的最后一行rbind使用rolltolast而不是{{1 }}。或者也许按帐户而不是所有帐户创建roll

看看上面的速度是否可以接受,我们可以从那里开始。

您可能会遇到1.8.2中已修复的1.8.2中的错误。我正在使用v1.8.3。

  组合包含缺失组和分组依据的联接时出现“内部”错误消息   是固定的,#2162。例如 :       X [Y,.N,通过= NonJoinColumn]    其中Y包含一些与X不匹配的行。这个bug也可能导致seg    故障。

让我知道,我们可以解决,或从R-Forge升级到1.8.3。

顺便说一下,很好的示例数据。这使得答案更快。


以上是上面提到的完整答案。我不得不承认这有点棘手,因为它将alldays的几个特征结合在一起。这应该在1.8.2中有效,但我只在1.8.3中测试过。

data.table

答案 1 :(得分:3)

这是一种方法:

billdata <- read.table(text=" acct amount begin end days
1 2242 11349 2009-10-06 2009-11-04 29
2 2242 12252 2009-11-04 2009-12-04 30
3 2242 21774 2009-12-04 2010-01-08 35
4 2242 18293 2010-01-08 2010-02-05 28
5 2243 27217 2009-10-06 2009-11-04 29
6 2243 117 2009-11-04 2009-12-04 30
7 2243 14543 2009-12-04 2010-01-08 35", sep=" ", header=TRUE, row.names=1)

#First, declare your columns "begin" and "end" as dates:
strptime(billdata$begin, format="%Y-%m-%d") -> billdata$begin
strptime(billdata$end, format="%Y-%m-%d") -> billdata$end

#Then create a column with the amount per day on the billing period:
billdata$avg_on_period<-billdata$amount/billdata$days

#Then split it into days:
temp <- data.frame(acct=c(),month=c(),day=c(), avg=c())
for(i in 1:nrow(billdata)){
    X <- billdata[i,]
    seq(X$begin,X$end,by="day") -> list_day
    rbind(temp, data.frame(acct=rep(X$acct,length(list_day)), 
        month=format(list_day, "%Y-%m"), day=format(list_day, "%d"), 
        avg=rep(X$avg_on_period, length(list_day)))) -> temp
    }

# And finally merge the different days of the months together:
output<-aggregate(temp$avg, by=list(temp$month,temp$acct), FUN=mean)

colnames(output) <- c("Month","Account","Average per day")

output
    Month Account Average per day
1 2009-10    2242       391.34483
2 2009-11    2242       406.69448
3 2009-12    2242       595.40000
4 2010-01    2242       645.51964
5 2010-02    2242       653.32143
6 2009-10    2243       938.51724
7 2009-11    2243        97.36172
8 2009-12    2243       364.06250
9 2010-01    2243       415.51429