2017-05-27 60 views
1

我在R中编写了一段代码,用于计算某些数据的累计和。有用。问题是,我需要“融化”25,000个数字X 12个月,所以我最终得到了300,000行(每个月将会有大约2000x12)。前六行是重新创建我的表格样本(一个巨大的Excel文件)。然后有一些魔法可以将事情转化为正确的格式,最后我有了这个双重for循环,它可以计算每个月的累计总和,因为它是一个双“PDRcount”或不是。当我尝试使用我的真实数据时,循环需要6小时...我怎样才能更快地做到这一点?重写一个永久需要的for循环

library(reshape2) 

PDR <- (c(1,2,3,4,5,2)) 
START <- as.Date(c("2008-01-01","2007-01-01","2010-01-01","2011-01-01","2017-02-01","2017-03-01")) 
SWITCHOUT <- as.Date(c(NA, "2017-02-28", NA, NA, "2017-03-31",NA)) 
JAN17 <- (c(100,124,165,178,0,0)) 
FEB17 <- (c(101,125,133,178,170,0)) 
MAR17 <- (c(99,0,165,180,166,99)) 
APR17 <- (c(100,0,156,178,0,78)) 

alldata <- data.frame(PDR=PDR, 
        START=START, 
        SWITCHOUT=SWITCHOUT, 
        JAN17=JAN17, 
        FEB17=FEB17, 
        MAR17=MAR17, 
        APR17=APR17) 

## count PDR occurrences  
alldata$PDRcount <- ave(alldata$PDR,alldata$PDR,FUN=length) 
alldata$PDRcount <- as.numeric(alldata$PDRcount) 

crossdata<-melt(alldata,id=(c("PDR", "START","SWITCHOUT","PDRcount"))) 
colnames(crossdata) <- c("PDR","START","SWITCHOUT","PDRcount","MONTH","SMC") 

## transform levels to date format 
levels(crossdata$MONTH)[1] <- "2017-01-01" 
levels(crossdata$MONTH)[2] <- "2017-02-01" 
levels(crossdata$MONTH)[3] <- "2017-03-01" 
levels(crossdata$MONTH)[4] <- "2017-04-01" 
crossdata$MONTH <- as.Date(crossdata$MONTH,format = "%Y-%m-%d") 


for (pdr in crossdata[,"PDR"]){ 

maxPDR <- max(crossdata$PDRcount[crossdata$PDR == pdr]) 
dates <- unique(crossdata$START[crossdata$PDR == pdr]) 

for (i in 1:maxPDR) { 

CumSum <- cumsum(crossdata$SMC[crossdata$PDR == pdr & crossdata$START == dates[i]]) 

    crossdata$SMCcum[crossdata$PDR == pdr & crossdata$START == dates[i] & crossdata$MONTH == "2017-01-01"] <- CumSum[1] 
    crossdata$SMCcum[crossdata$PDR == pdr & crossdata$START == dates[i] & crossdata$MONTH == "2017-02-01"] <- CumSum[2] 
    crossdata$SMCcum[crossdata$PDR == pdr & crossdata$START == dates[i] & crossdata$MONTH == "2017-03-01"] <- CumSum[3] 
    crossdata$SMCcum[crossdata$PDR == pdr & crossdata$START == dates[i] & crossdata$MONTH == "2017-04-01"] <- CumSum[4]  
} 
} 

编辑:对不起,出现了错误...

+0

为什么第一个值是NA? – Sotos

+0

因为客户端仍然活动,所以没有切换日期 – oceanfront

回答

2

这是部分答案。我不明白这个部分“......基于它是否是双”PDRcount“。”

这里使用dplyr库的情况下PDR!= 2的部分答案。在进行任何计算之前,我还通过在您的crossdata变量上使用dput来简化数据输入。

crossdata1<-structure(list(PDR = c(1, 2, 3, 4, 5, 2, 1, 2, 3, 4, 5, 2, 1, 
            2, 3, 4, 5, 2, 1, 2, 3, 4, 5, 2), 
          START = structure(c(13879, 13514, 14610, 14975, 17198, 17226, 13879, 13514, 14610, 14975, 
            17198, 17226, 13879, 13514, 14610, 14975, 17198, 17226, 13879, 
            13514, 14610, 14975, 17198, 17226), class = "Date"), 
          SWITCHOUT = structure(c(NA, 17225, NA, NA, 17256, NA, NA, 17225, NA, NA, 17256, NA, NA, 17225, 
             NA, NA, 17256, NA, NA, 17225, NA, NA, 17256, NA), class = "Date"), 
          PDRcount = c(1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2), 
          MONTH = structure(c(17167, 17167, 
            17167, 17167, 17167, 17167, 17198, 17198, 17198, 17198, 17198, 
            17198, 17226, 17226, 17226, 17226, 17226, 17226, 17257, 17257, 
            17257, 17257, 17257, 17257), class = "Date"), 
          SMC = c(100, 124, 165, 178, 0, 0, 101, 125, 133, 178, 170, 0, 99, 0, 165, 
            180, 166, 99, 100, 0, 156, 178, 0, 78)), 
         row.names = c(NA, -24L), .Names = c("PDR", "START", "SWITCHOUT", "PDRcount", "MONTH", "SMC"), 
         class = "data.frame") 

#test to see if starting data is the same 
identical(crossdata, crossdata1) 
library(dplyr) 

#group by and add the cumsum column to answer dataframe 
ans<-group_by(crossdata1, PDR) %>% 
    mutate(SMCcum = cumsum(SMC)) 

#rows where the 2 final dataframes do not match 
crossdata[-which(crossdata$SMCcum== ans$SMCcum),] 

如果应用附加过滤器来删除“... double”PDRcount“或不”的情况,最有可能的上述行可以工作。适用。

我发现这个职位有帮助:cumsum in grouped data with dplyr

好运。

+0

谢谢!这是有效的,为了得到我需要的结果,我只需要通过START ans <-group_by(crossdata1,PDR,START)%>%进行分组。我将首先尝试使用3000行样本,然后我们将看到它处理300k ... – oceanfront

3

你会不断覆盖你的结果。 一个明显的改进是循环使用unique(crossdata[,"PDR"])而不是调用每一行的循环。

我不能确定你的内循环能够为maxPDR > 1预期的结果,你都在不断改写,其中STARTmaxPDR“日dates条目匹配的值 - 注意,你没有那种dates因此没有保证dates[maxPDR]是最大的(最近的)条目。

我在dplyr中写了一个替代解决方案,有两个步骤来简化转换为所需的格式。

alldata <- data.frame(PDR=PDR, START=START, SWITCHOUT=SWITCHOUT, JAN17=JAN17, 
         FEB17=FEB17, MAR17=MAR17, APR17=APR17) 

library(dplyr) 
library(tidyr) # to reshape the data 

crossdata_2 <- alldata %>% 
    gather(MONTH,SMC,ends_with("17")) %>% 
    mutate(MONTH = as.character(strptime(paste0(MONTH,"-01"), format = "%b%y-%d"))) %>% 
    # the following line adds your PDRcount but is unnecessary for further computation 
    group_by(PDR) %>% mutate(PDRcount = n_distinct(START)) %>% 
    group_by(PDR,START) %>% mutate(SMCcum = cumsum(SMC)) 

注意,我计算cumsum()为每PDRSTART。如果你只需要每个PDR的一个结果,你只需要添加一个合适的过滤器。

我想指出,strptime中的缩写月份转换%b是特定于语言环境的。要正常工作,您可能需要更改LC_TIME