2015-12-24 12 views
8

Tôi có một dataframe lớn (3M + hàng). Tôi đang cố đếm số lần một ActivityType nhất định xuất hiện trong cửa sổ 21 ngày. Tôi đã lập mô hình giải pháp của mình từ Rolling Sum by Another Variable in R. Nhưng phải mất một thời gian dài chỉ cho một ActivityType. Tôi không nghĩ rằng các hàng 3M + là thứ sẽ mất một lượng thời gian vô hạn. Dưới đây là những gì tôi đã thử:Cách nhanh nhất để thực hiện 21 ngày sum lăn cho một ActivityType

dt <- read.table(text=' 

         Name  ActivityType  ActivityDate     
         John  Email   1/1/2014   
         John  Email   1/3/2014     
         John  Webinar   1/5/2014   
         John  Webinar   1/20/2014   
         John  Webinar   3/25/2014   
         John  Email   4/1/2014   
         John  Email   4/20/2014   
         Tom  Email   1/1/2014   
         Tom  Webinar   1/5/2014   
         Tom  Webinar   1/20/2014   
         Tom  Webinar   3/25/2014   
         Tom  Email    4/1/2014   
         Tom  Email    4/20/2014   

         ', header=T, row.names = NULL) 

     library(data.table) 
     library(reshape2) 
     dt$ActivityType <- factor(dt$ActivityType) 
     dt$ActivityDate <- as.Date(dt$ActivityDate, "%m/%d/%Y") 
     dt <- dt[order(dt$Name, dt$ActivityDate),] 

    dt <- dcast(dt, Name + ActivityDate ~ ActivityType, fun.aggregate=length) 
    setDT(dt) 
    #Build reference table 
     Ref <- dt[,list(Compare_Value=list(I(Email)),Compare_Date=list(I(ActivityDate))), by=c("Name")] 
    #Use mapply to get last 21 days of value by Name  
    dt[,Email_RollingSum := mapply(ActivityDate=ActivityDate,Name=Name, function(ActivityDate, Name) { 
      d <- as.numeric(Ref$Compare_Date[[Name]] - ActivityDate) 
      sum((d <= 0 & d >= -21)*Ref$Compare_Value[[Name]])})] 

Và điều này chỉ dành cho ActivityType = Email, sau đó tôi phải làm tương tự cho các cấp độ khác của ActivityType. Liên kết mà tôi đã nhận được giải pháp từ nói về việc sử dụng "mcapply" thay vì "mapply". Vui lòng cho tôi biết làm thế nào tôi có thể sử dụng mcapply hoặc bất kỳ giải pháp khác mà sẽ làm cho nó nhanh hơn.

Dưới đây là kết quả mong đợi. Đối với mỗi hàng, tôi lấy ActivityDate và 21 ngày trước đó và thời gian 21 ngày là cửa sổ thời gian của tôi. Tôi đếm mọi lúc ActivityType = "Email" xuất hiện trong cửa sổ thời gian đó.

   Name  ActivityType  ActivityDate Email_RollingSum    
       John  Email   1/1/2014   1 
       John  Email   1/3/2014   2  
       John  Webinar   1/5/2014   2 
       John  Webinar   1/20/2014  2 
       John  Webinar   3/25/2014  0 
       John  Email   4/1/2014   1 
       John  Email   4/20/2014  2 
       Tom  Email   1/1/2014   1 
       Tom  Webinar   1/5/2014   1 
       Tom  Webinar   1/20/2014  1 
       Tom  Webinar   3/25/2014  0 
       Tom  Email    4/1/2014   1 
       Tom  Email    4/20/2014  2 
+0

Tôi thường chuyển đổi ngày thành số khi xử lý hàng triệu hàng. as.numeric (dt $ ActivityDate). Không phải là một giải pháp nhưng có lẽ là một sự cải tiến. – Jordan

+1

Kể từ khi OP là một cách rõ ràng về tốc độ nó sẽ là tốt để cập nhật nó với timings bạn có với các phương pháp khác nhau. – eddi

+0

Cách tiếp cận bởi WaltS mất 1,89 giờ. Vì lý do nào đó, tôi đã hết bộ nhớ hai lần trong khi thử cách tiếp cận của eddi. Cách tiếp cận của Khasana đang chạy (một ít hơn 2 giờ) khi excel đâm máy tính của tôi. – gibbz00

Trả lời

4

Thử cách tiếp cận trong đó bảng dữ liệu được sử dụng cho cả danh sách tên và ngày và nguồn của số lượng email. Này được thực hiện trong data.table bằng DT trong i đối số của DT cùng với by = .EACHI. Mã có thể trông giống như:

library(data.table) 
# convert character dates to Date types 
dt$ActivityDate <- as.Date(dt$ActivityDate, "%m/%d/%Y") 
# convert to a 'data.table' and define key 
setDT(dt, key = "Name") 
# count emails and webinars 
dt <- dt[dt[,.(Name, type = ActivityType, date = ActivityDate)], 
     .(type, date, 
      Email = sum(ActivityType == "Email" & between(ActivityDate, date-21, date)), 
      Webinar = sum(ActivityType == "Webinar" & between(ActivityDate, date-21, date))), 
     by=.EACHI] 

Sau đây sử dụng phương pháp tương tự như trên nhưng bao gồm một số thay đổi có thể cải thiện tốc độ 30-40% tùy thuộc vào dữ liệu của bạn.

setDT(dt, key = "Name") 
    dt[, ":="(ActivityDate = as.Date(dt$ActivityDate, "%m/%d/%Y"), 
      ActivityType = as.character(ActivityType))] 
    dt4 <- dt[.(Name=Name, type=ActivityType, date=ActivityDate), {z=between(ActivityDate, date-21, date); 
                    .(type, date, 
                    Email=sum((ActivityType %chin% "Email") & z), 
                    Webinar=sum((ActivityType %chin% "Webinar") & z)) } 
      , by=.EACHI] 
+1

'<-' chống lại cột data.table là một thực tế xấu – jangorecki

+0

@jangorecki những gì nên được thực hiện thay vì <-? – gibbz00

+1

@ gibbz00 toán tử ': =', đọc [Tham chiếu ngữ nghĩa tham chiếu] (https://rawgit.com/wiki/Rdatatable/data.table/vignettes/datatable-reference-semantics.html). – jangorecki

6
setDT(dt) 
dt[, ActivityDate := as.Date(ActivityDate, '%m/%d/%Y')] 

# add index to keep track of rows 
dt[, idx := .I] 

# match the dates we're looking for using a rolling join and extract the row numbers 
rr = dt[.(Name = Name, ActivityDate = ActivityDate - 21, refIdx = idx), 
     .(idx, refIdx), on = c('Name', 'ActivityDate'), roll = -Inf] 
# idx refIdx 
# 1: 1  1 
# 2: 1  2 
# 3: 1  3 
# 4: 1  4 
# 5: 5  5 
# 6: 5  6 
# 7: 6  7 
# 8: 8  8 
# 9: 8  9 
#10: 8  10 
#11: 11  11 
#12: 11  12 
#13: 12  13 

# extract the above rows and count occurrences using dcast 
dcast(rr[, {seq = idx:refIdx; dt[seq]}, by = 1:nrow(rr)], nrow ~ ActivityType) 
# nrow Email Webinar 
#1  1  1  0 
#2  2  2  0 
#3  3  2  1 
#4  4  2  2 
#5  5  0  1 
#6  6  1  1 
#7  7  2  0 
#8  8  1  0 
#9  9  1  1 
#10 10  1  2 
#11 11  0  1 
#12 12  1  1 
#13 13  2  0 
+0

Cảm ơn bạn rất nhiều vì câu trả lời của bạn! – gibbz00

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