2015-12-24 13 views
8

Mam dużą ramkę danych (3M + rzędy). Próbuję policzyć liczbę razy, gdy określony parametr ActivityType pojawia się w 21-dniowym oknie. Mam modelowane moje rozwiązanie od Rolling Sum by Another Variable in R. Ale zajmuje tylko jeden typ ActivityType. Nie sądziłem, że 3M + wiersze to coś, co zajmie nadmierną ilość czasu. Poniżej jest co starałem:Najszybszy sposób na zrobienie 21-dniowej sumy kroczącej dla 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]])})] 

I to jest właśnie dla ActivityType = Email, to muszę zrobić to samo dla innych poziomów ActivityType. Link, z którego otrzymałem rozwiązanie, mówił o używaniu "mcapply" zamiast "mapply". Uprzejmie proszę mi powiedzieć, w jaki sposób mogę użyć mcapply lub innego rozwiązania, które przyspieszy działanie.

Poniżej przedstawiono oczekiwane wyniki. Dla każdego wiersza biorę ActivityDate i 21 dni wcześniej, a ten 21-dniowy okres jest moim oknem czasowym. Liczę cały czas ActivityType = "Email" pojawia się w tym oknie czasowym.

   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

często konwertować moje daty, aby numeryczne gdy ma do czynienia z milionami wierszy. as.numeric (dt $ ActivityDate). Nie rozwiązanie, ale być może poprawa. – Jordan

+1

Ponieważ OP jest wyraźnie o prędkości, dobrze byłoby zaktualizować go o czasy, które otrzymałeś różnymi metodami. – eddi

+0

Podejście WaltS trwało 1,89 godziny. Z jakiegoś powodu zabrakło mi pamięci dwa razy, próbując podejść Eddiego. Podejście Khasany trwało (nieco ponad 2 godziny), gdy program Excel wyłączył komputer. – gibbz00

Odpowiedz

4

Spróbuj podejścia, w którym tabela danych jest używana zarówno do listy nazwisk i dat, jak i do źródła liczby e-maili. Odbywa się to w data.table, używając argumentu DT w argumencie wraz z by = .EACHI. Kod może wyglądać tak:

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] 

następujących zastosowań tego samego podejścia jak powyżej, ale zawiera kilka zmian, które mogą poprawić prędkość o 30-40% w zależności od Twoich danych.

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

'<-' przeciwko kolumnie data.table to zła praktyka – jangorecki

+0

@jangorecki co należy zrobić zamiast <-? – gibbz00

+1

@ gibbz00 Operator ': =', przeczytaj [Werkette semantyki odniesienia] (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

Dziękuję bardzo za odpowiedź! – gibbz00

Powiązane problemy