重写一个永远需要的for循环

时间:2017-05-27 17:33:12

标签: r for-loop time vectorization execution

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

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]     
}
}

编辑:抱歉有错误...

2 个答案:

答案 0 :(得分:3)

您不断覆盖结果。 一个明显的改进是循环grid[i][j] = randomGen.nextInt(); 而不是为每一行调用循环。

我不确定您的内部循环是否为unique(crossdata[,"PDR"])提供了预期的结果,而您不断覆盖maxPDR > 1START {{1}匹配的值输入 - 请注意,您没有对maxPDR进行排序,因此无法保证dates是最大(最近)的条目。

我在dates中编写了一个替代解决方案,其中包含两个步骤,可以轻松转换为所需的格式。

dates[maxPDR]

请注意,我为每dplyralldata <- 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()。如果您只需要为每个PDR生成一个结果,则只需添加适当的过滤器。

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

答案 1 :(得分:2)

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

这里是使用dplyr库的PDR!= 2的情况的部分答案。我还在任何计算之前使用交叉数据变量上的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),]

如果应用额外的过滤器来移除“...双倍”PDRcount“或不”的情况,上述行很可能会有效。适用。

我发现此帖有用:cumsum in grouped data with dplyr

祝你好运。