7

为了确定对能源使用的季节性影响,我需要将我从计费数据库中获得的能源使用信息与每月温度相匹配。

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

   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() 来汇总 accts和几个月,但老实说,看不出如何有效地做到这一点。

在 MySQL 中,我试过这个:

创建或替换视图 v3 为 select 1 n union all select 1 union all select 1;
创建或替换视图 v 为 select 1 n from v3 a, v3 b union all select 1;
设置@n = 0;
如果存在日历,则删除表;创建表日历(dt日期主键);
插入日历
select cast('2008-1-1' + interval @n:=@n+1 day as date) as dt from va, vb, vc, vd, ve, v;

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

但是由于我不明白的原因,我的服务器不喜欢这个表,并且在内部连接上挂断了,即使我进行不同的计算也是如此。我正在调查它是否有任何临时内存限制。

谢谢!

4

2 回答 2

9

这是一个开始使用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 行是重复的,因为与帐户 2242 不同,帐户 2243 不会延伸到 2010-02。要完成它,您可以rbind在每个帐户的最后一行使用rolltolast而不是roll. 或者也许alldays按帐户而不是跨所有帐户创建。

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

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

修复了组合包含缺失组和分组依据的联接时的“内部”错误消息,#2162。例如:X[Y,.N,by=NonJoinColumn] 其中 Y 包含一些与 X 不匹配的行。此错误也可能导致段错误。

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

顺便说一句,很好的示例数据。这使它更快地回答。


这是上面提到的完整答案。我不得不承认这有点棘手,因为它结合了data.table. 这应该在 1.8.2 中起作用,但我只在 1.8.3 中测试过。

DT[ setkey(DT[,seq(begin[1],last(end),by="day"),by=acct]),
    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
于 2012-10-02T17:38:06.363 回答
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
于 2012-09-24T08:11:20.063 回答