2012-09-24 51 views
6

Để thiết lập các hiệu ứng theo mùa về sử dụng năng lượng, tôi cần căn chỉnh thông tin sử dụng năng lượng mà tôi có từ cơ sở dữ liệu thanh toán với nhiệt độ hàng tháng.Chia chuỗi thời gian không thường xuyên thành trung bình hàng tháng - R

Tôi đang làm việc với tập dữ liệu thanh toán có hóa đơn có độ dài khác nhau và ngày bắt đầu và ngày kết thúc và tôi muốn nhận được mức trung bình hàng tháng cho mỗi tài khoản trong mỗi tháng. Ví dụ, tôi có một cơ sở dữ liệu thanh toán có các đặc điểm sau:

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 

Tôi muốn tìm ra cách để ép buộc các chuỗi thời gian hơi bất thường (đối với mỗi tài khoản) để có được số tiền trung bình mỗi ngày trong mỗi tháng được kéo dài trong mỗi hóa đơn, chẳng hạn rằng:

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 

tôi khá thuyết bất khả tri đến công cụ nào có thể làm được điều này, vì tôi chỉ phải làm điều này một lần.

Một nếp nhăn bổ sung là bảng dài khoảng 150.000 hàng, không thực sự lớn bằng hầu hết các tiêu chuẩn, nhưng đủ lớn để tạo ra giải pháp vòng lặp trong R khó khăn. Tôi đã điều tra bằng cách sử dụng các gói zoo, xts và tempdisagg trong R. Tôi bắt đầu viết một vòng lặp thực sự xấu xí để phân chia từng hóa đơn, sau đó tạo một hàng cho mỗi tháng trong một hóa đơn hiện có, và sau đó tapply() để tóm tắt và nhiều tháng, nhưng thành thật mà nói, không thể thấy làm thế nào để làm điều đó một cách hiệu quả.

Trong MySQL, tôi đã cố gắng này:

tạo hoặc thay thế xem v3 như chọn 1 n union all select 1 union all select 1;
tạo hoặc thay thế chế độ xem v khi chọn 1 n từ v3 a, v3 b union tất cả chọn 1;
đặt @n = 0;
bảng thả nếu có lịch; tạo lịch biểu (dt date primary key);
chèn vào lịch
chọn diễn viên ('2008-1-1' + khoảng thời gian @n: = @ n + 1 ngày làm ngày) dưới dạng dt từ v a, v b, v c, v d, v đ, v;

chọn acct, số tiền, bắt đầu, kết thúc, billAmtPerDay, sum (billAmtPerDay), MonthAmt, count () Ngày, sum (billAmtPerDay)/count () AverageAmtPerDay, năm (dt), tháng (dt) FROM (chọn *, số tiền/ngày billAmtPerDay từ hóa đơn b tham gia bên trong lịch c trên dt giữa bắt đầu và kết thúc và bắt đầu <> dt) x nhóm theo acct, số tiền, bắt đầu, kết thúc, billAmtPerDay, năm (dt), tháng (dt);

Nhưng vì lý do tôi không hiểu, máy chủ của tôi không thích bảng này và bị treo trên tham gia bên trong, ngay cả khi tôi thực hiện các phép tính khác nhau. Tôi đang điều tra nếu có bất kỳ giới hạn bộ nhớ tạm thời nào trên đó.

Cảm ơn!

+1

Thời gian thanh toán của bạn trùng với tháng thực hay là một số chức năng "Ngày X của mỗi tháng là khi một giai đoạn mới bắt đầu" loại tình huống? –

+0

Thời hạn thanh toán là không thường xuyên, vì vậy hầu hết các hóa đơn đều dài trung bình 30 +/- 2 ngày, nhưng với một số hóa đơn miễn là 90 ngày trở lên. – bikeclub

+1

Sau đó, bạn cần một số phương pháp để tính toán thời hạn thanh toán được chỉ định một ngày, vì vậy bạn có thể thực hiện nhóm thích hợp. –

Trả lời

8

Dưới đây là một sự khởi đầu sử dụng 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) 

Thứ nhất, loại thay đổi các cột beginend đến ngày. Không giống như data.frame, điều này không sao chép toàn bộ tập dữ liệu.

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

Sau đó, tìm khoảng thời gian, tìm hóa đơn hiện hành cho mỗi ngày và tổng hợp.

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 

Tôi nghĩ bạn sẽ tìm thấy logic kết nối phổ biến khá cồng kềnh trong SQL và chậm hơn.

Tôi nói đó là gợi ý vì nó không hoàn toàn chính xác. Thông báo hàng 10 được lặp lại vì tài khoản 2243 không kéo dài đến 2010-02 không giống như tài khoản 2242. Để hoàn tất, bạn có thể rbind ở hàng cuối cùng cho mỗi tài khoản và sử dụng rolltolast thay vì roll. Hoặc có thể tạo tài khoản alldays thay vì trên tất cả các tài khoản.

Xem tốc độ có được chấp nhận ở trên hay không và chúng tôi có thể đi từ đó.

Có thể bạn sẽ gặp lỗi trong 1.8.2 đã được khắc phục trong 1.8.3. Tôi đang sử dụng v1.8.3.

Thông báo lỗi "Nội bộ" khi kết hợp tham gia chứa nhóm và nhóm bị thiếu là cố định, # 2162. Ví dụ: X [Y, .N, by = NonJoinColumn] trong đó Y chứa một số hàng không khớp với X. Lỗi này cũng có thể dẫn đến lỗi seg .

Hãy cho tôi biết và chúng tôi có thể làm việc xung quanh hoặc nâng cấp lên 1.8.3 từ R-Forge.

Btw, dữ liệu mẫu đẹp. Điều đó làm cho nó nhanh hơn để trả lời.


Đây là câu trả lời đầy đủ được đề cập ở trên. Đó là một chút khó khăn tôi phải thừa nhận, vì nó kết hợp với nhau một số tính năng của data.table. Điều này sẽ làm việc trong 1.8.2 như nó xảy ra, nhưng tôi đã chỉ được thử nghiệm trong 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 
+0

Hi Matthew, xin lỗi không trả lời trước đó - Tôi đã chạy cả hai phương pháp, và giải pháp data.table * là * nhanh hơn nhiều, nhưng chúng đưa ra các câu trả lời khác nhau, vì vậy tôi đang kiểm tra mã trên cả hai ngay bây giờ. – bikeclub

+1

@ D.Hsu Từ một nháy mắt, tôi nghĩ câu trả lời khác có thể là hai lần đếm ngày kết thúc của mỗi hóa đơn, vì dữ liệu ví dụ có thể được coi là mơ hồ. Câu trả lời của tôi sử dụng [bắt đầu, kết thúc) không phải [bắt đầu, kết thúc]. –

+0

Matt, tôi đã kiểm tra và mã của bạn hoạt động tốt. Lý do tại sao nó đã cho tôi một vài ngày để kiểm tra là tôi thực sự đã nghĩ đến một tính toán hơi khác so với một trong những tôi đặt ra trong vấn đề. Tuy nhiên, hàm data.table rất nhanh và dễ sửa đổi hơn cho các mục đích (cuối cùng) của tôi. Cảm ơn bạn đã phát triển gói cũng như trả lời câu hỏi của tôi. – bikeclub

3

Dưới đây là một cách để làm điều đó:

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 
+0

Cảm ơn bạn đã trả lời nhanh này. Tôi đã trì hoãn việc trả lời khi tôi thử mã. Tuy nhiên, có lẽ tôi đã đề cập trước đó rằng tôi muốn áp dụng điều này cho một tập dữ liệu tương đối lớn: 150.000 hàng, và điều này chỉ làm cho nó thông qua khoảng 10% mã trong 5-6 giờ (trên một máy chủ rất nhanh). Tôi nghĩ rằng điều này có thể được giải quyết tốt hơn bằng cách sử dụng SQL. – bikeclub

+0

Thật vậy nếu tập dữ liệu của bạn dài 150 000 hàng, tập dữ liệu 'temp' trung gian sẽ dài vài triệu hàng. Có lẽ có một giải pháp tốt bằng cách sử dụng 'dữ liệu.table' nhưng tôi không bao giờ sử dụng nó vì vậy tôi không thể giúp ở đây. – plannapus

+0

plannapus, @ D.Hsu, Vâng, đó là một câu hỏi hay và lý tưởng cho 'data.table'. Tôi sẽ thêm một câu trả lời sau. –

Các vấn đề liên quan