2012-05-22 27 views
15

Hãy lấy dữ liệu sau:R: sử dụng data.table: = hoạt động để tính toán các cột mới

dt <- data.table(TICKER=c(rep("ABC",10),"DEF"), 
     PERIOD=c(rep(as.Date("2010-12-31"),10),as.Date("2011-12-31")), 
     DATE=as.Date(c("2010-01-05","2010-01-07","2010-01-08","2010-01-09","2010-01-10","2010-01-11","2010-01-13","2010-04-01","2010-04-02","2010-08-03","2011-02-05")), 
     ID=c(1,2,1,3,1,2,1,1,2,2,1),VALUE=c(1.5,1.3,1.4,1.6,1.4,1.2,1.5,1.7,1.8,1.7,2.3)) 
setkey(dt,TICKER,PERIOD,ID,DATE) 

Bây giờ cho từng tổ hợp ticker/thời gian, tôi cần những điều sau đây trong một cột mới:

  • PRIORAVG: Giá trị trung bình của VALUE mới nhất của từng ID, không bao gồm ID hiện tại, cung cấp ID không quá 180 ngày.
  • PREV: Giá trị trước đó từ cùng một ID.

Kết quả sẽ giống như thế này:

 TICKER  PERIOD  DATE ID VALUE PRIORAVG PREV 
[1,] ABC 2010-12-31 2010-01-05 1 1.5  NA NA 
[2,] ABC 2010-12-31 2010-01-08 1 1.4  1.30 1.5 
[3,] ABC 2010-12-31 2010-01-10 1 1.4  1.45 1.4 
[4,] ABC 2010-12-31 2010-01-13 1 1.5  1.40 1.4 
[5,] ABC 2010-12-31 2010-04-01 1 1.7  1.40 1.5 
[6,] ABC 2010-12-31 2010-01-07 2 1.3  1.50 NA 
[7,] ABC 2010-12-31 2010-01-11 2 1.2  1.50 1.3 
[8,] ABC 2010-12-31 2010-04-02 2 1.8  1.65 1.2 
[9,] ABC 2010-12-31 2010-08-03 2 1.7  1.70 1.8 
[10,] ABC 2010-12-31 2010-01-09 3 1.6  1.35 NA 
[11,] DEF 2011-12-31 2011-02-05 1 2.3  NA NA 

Lưu ý PRIORAVG trên hàng 9 là tương đương với 1,7 (tương đương với các VALUE trên hàng 5, đó là quan sát trước chỉ trong quá khứ 180 ngày khác ID)

Tôi đã phát hiện gói data.table, nhưng tôi dường như không thể hiểu đầy đủ chức năng :=. Khi tôi giữ nó đơn giản, nó có vẻ hoạt động. Để có được giá trị trước đó cho mỗi ID (tôi dựa trên giải pháp cho this question):

dt[,PREV:=dt[J(TICKER,PERIOD,ID,DATE-1),roll=TRUE,mult="last"][,VALUE]] 

này hoạt động tuyệt vời, và nó chỉ mất 0,13 giây để thực hiện thao tác này trên bộ dữ liệu của tôi với ~ hàng 250k; chức năng quét véc-tơ của tôi có kết quả giống nhau nhưng chậm hơn khoảng 30.000 lần.

Ok, vì vậy tôi đã có yêu cầu đầu tiên của mình. Hãy đến yêu cầu thứ hai, phức tạp hơn. Ngay bây giờ, phương pháp nhịn ăn cho đến nay đối với tôi là sử dụng một vài lần quét vectơ và ném hàm thông qua hàm plyradply để nhận kết quả cho mỗi hàng.

calc <- function(df,ticker,period,id,date) { 
    df <- df[df$TICKER == ticker & df$PERIOD == period 
     & df$ID != id & df$DATE < date & df$DATE > date-180, ] 
    df <- df[order(df$DATE),] 
    mean(df[!duplicated(df$ID, fromLast = TRUE),"VALUE"]) 
} 

df <- data.frame(dt) 
adply(df,1,function(x) calc(df,x$TICKER,x$PERIOD,x$ID,x$DATE)) 

tôi đã viết hàm cho một data.frame và nó dường như không làm việc với một data.table. Đối với một tập hợp con của 5000 hàng, điều này mất khoảng 44 giây nhưng dữ liệu của tôi bao gồm> 1 triệu hàng. Tôi tự hỏi nếu điều này có thể được thực hiện hiệu quả hơn thông qua việc sử dụng :=.

dt[J("ABC"),last(VALUE),by=ID][,mean(V1)] 

Điều này hoạt động để chọn mức trung bình của VALUE mới nhất cho mỗi ID cho ABC.

dt[,PRIORAVG:=dt[J(TICKER,PERIOD),last(VALUE),by=ID][,mean(V1)]] 

Tuy nhiên, điều này không tính trung bình của tất cả VALUE cuối cùng cho tất cả các dấu tick/dấu chấm thay vì chỉ cho dấu tick/chu kỳ hiện tại. Vì vậy, nó kết thúc với tất cả các hàng nhận được cùng một giá trị trung bình. Tôi có làm điều gì sai hay đây là giới hạn của :=?

+1

Gợi ý: tham gia phạm vi được kế thừa cho quan sát hiện hành với 180 ngày qua (sử dụng tiền tố 'i.':' [, j = list (..., age = PERIOD-i.PERIOD, ...),] [age <180] ', và' mult = "last" 'chứ không phải' last() ', có thể. –

+1

Bảng dữ liệu được đề cập có vẻ khác biệt với mã trích xuất ở trên nó. Và nó thiếu') ' dường như. –

+0

đã thêm dữ liệu hiển thị kết quả mong đợi của yêu cầu 180 ngày – Dirk

Trả lời

12

Câu hỏi hay. Hãy thử điều này:

dt 
    TICKER  PERIOD  DATE ID VALUE 
[1,] ABC 2010-12-31 2010-01-05 1 1.5 
[2,] ABC 2010-12-31 2010-01-08 1 1.4 
[3,] ABC 2010-12-31 2010-01-10 1 1.4 
[4,] ABC 2010-12-31 2010-01-13 1 1.5 
[5,] ABC 2010-12-31 2010-01-07 2 1.3 
[6,] ABC 2010-12-31 2010-01-11 2 1.2 
[7,] ABC 2010-12-31 2010-01-09 3 1.6 
[8,] DEF 2011-12-31 2011-02-05 1 2.3 

ids = unique(dt$ID) 
dt[,PRIORAVG:=NA_real_] 
for (i in 1:nrow(dt)) 
    dt[i,PRIORAVG:=dt[J(TICKER[i],PERIOD[i],setdiff(ids,ID[i]),DATE[i]), 
         mean(VALUE,na.rm=TRUE),roll=TRUE,mult="last"]] 
dt 
    TICKER  PERIOD  DATE ID VALUE PRIORAVG 
[1,] ABC 2010-12-31 2010-01-05 1 1.5  NA 
[2,] ABC 2010-12-31 2010-01-08 1 1.4  1.30 
[3,] ABC 2010-12-31 2010-01-10 1 1.4  1.45 
[4,] ABC 2010-12-31 2010-01-13 1 1.5  1.40 
[5,] ABC 2010-12-31 2010-01-07 2 1.3  1.50 
[6,] ABC 2010-12-31 2010-01-11 2 1.2  1.50 
[7,] ABC 2010-12-31 2010-01-09 3 1.6  1.35 
[8,] DEF 2011-12-31 2011-02-05 1 2.3  NA 

Sau đó, những gì bạn đã có một chút đơn giản hóa ...

dt[,PREV:=dt[J(TICKER,PERIOD,ID,DATE-1),VALUE,roll=TRUE,mult="last"]] 

    TICKER  PERIOD  DATE ID VALUE PRIORAVG PREV 
[1,] ABC 2010-12-31 2010-01-05 1 1.5  NA NA 
[2,] ABC 2010-12-31 2010-01-08 1 1.4  1.30 1.5 
[3,] ABC 2010-12-31 2010-01-10 1 1.4  1.45 1.4 
[4,] ABC 2010-12-31 2010-01-13 1 1.5  1.40 1.4 
[5,] ABC 2010-12-31 2010-01-07 2 1.3  1.50 NA 
[6,] ABC 2010-12-31 2010-01-11 2 1.2  1.50 1.3 
[7,] ABC 2010-12-31 2010-01-09 3 1.6  1.35 NA 
[8,] DEF 2011-12-31 2011-02-05 1 2.3  NA NA 

Nếu đây là ok như một nguyên mẫu sau đó một sự cải thiện tốc độ lớn sẽ được giữ vòng lặp nhưng sử dụng set() thay vì :=, để giảm chi phí:

for (i in 1:nrow(dt)) 
    set(dt,i,6L,dt[J(TICKER[i],PERIOD[i],setdiff(ids,ID[i]),DATE[i]), 
        mean(VALUE,na.rm=TRUE),roll=TRUE,mult="last"]) 
dt 
    TICKER  PERIOD  DATE ID VALUE PRIORAVG PREV 
[1,] ABC 2010-12-31 2010-01-05 1 1.5  NA NA 
[2,] ABC 2010-12-31 2010-01-08 1 1.4  1.30 1.5 
[3,] ABC 2010-12-31 2010-01-10 1 1.4  1.45 1.4 
[4,] ABC 2010-12-31 2010-01-13 1 1.5  1.40 1.4 
[5,] ABC 2010-12-31 2010-01-07 2 1.3  1.50 NA 
[6,] ABC 2010-12-31 2010-01-11 2 1.2  1.50 1.3 
[7,] ABC 2010-12-31 2010-01-09 3 1.6  1.35 NA 
[8,] DEF 2011-12-31 2011-02-05 1 2.3  NA NA 

Đó nên là nhanh hơn rất nhiều so với các lần quét vectơ lặp lại được hiển thị trong câu hỏi.

Hoặc hoạt động có thể được vector hóa. Nhưng điều đó sẽ ít dễ viết và dễ đọc hơn do các tính năng của tác vụ này.

Btw, không có bất kỳ dữ liệu nào trong câu hỏi có thể kiểm tra yêu cầu 180 ngày. Nếu bạn thêm một số và hiển thị kết quả mong đợi một lần nữa thì tôi sẽ thêm phép tính tuổi bằng cách sử dụng phạm vi được thừa kế mà tôi đã đề cập trong các nhận xét.

+0

Câu trả lời hay. Chỉ mất 20 phút để tính toán phần đầu tiên của tập dữ liệu (180 nghìn hàng) so với vài giờ cho phương pháp vectơ. Tôi thích sử dụng setdiff() để chọn tất cả trừ ID hiện tại, nhưng tôi nghĩ rằng nó có thể làm chậm mọi thứ xuống một chút với một số lượng lớn ID (có 6000 trong số liệu của tôi và chỉ có trung bình 16 ID trên mỗi mã). – Dirk

+0

Tốt. 20 phút vẫn còn rất dài cho công việc này. Sử dụng 'set()'? Dù sao, như câu thần chú, 'Rprof',' Rprof', 'Rprof'. Có trên 'setdiff()' (nếu 'Rprof' hiển thị là gây ra thời gian), bạn có thể làm điều đó trả trước và lưu trữ một danh sách hoặc môi trường của id" khác "cho mỗi id và sau đó chỉ cần tìm nó lên. Hoặc có thể có một cách đơn giản hơn tôi đang mất tích. –

+0

Đó thực sự là bằng cách sử dụng 'set()'. 'setdiff()' chính nó không mất nhiều thời gian, nó là subsetting bằng cách sử dụng đầu ra của 'setdiff()'. Thử nghiệm với một tập hợp con của 5k hàng, tăng 'ids' từ 738 đến 5866 cộng thêm 60% thời gian tính toán. – Dirk

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