2016-11-30 44 views
0

我有一個很大的data.frame,有數千行,我每次循環創建第1行的一個子集,然後遞增一個額外的行,每次迭代。當涉及多個任務和/或if語句時避免出現循環

在每個子集上,我執行幾個任務,這些任務來自對編程的pythonic理解,我用'for'循環執行。例如。

df <- data.frame(a=c(1:10), b=c(11:20), c=c(21:30), d=c(31:40)) 
for (index in 1:nrow(df)) { 
    thisSubset <- df[1: index,] 

    #loop 1 
    new1 <- ncumeric(nrow(thisSubset)) 
    for (i in 1:nrow(thisSubset)) {    
     var1 = 5 - thisSubset$b[i]   
     new1[i] <- 1/exp(var1*log(2)); 
    } 
    #loop 2   
    new2 <- numeric() 
    for (i in 1:nrow(thisSubset)) if (thisSubset$c[i] >25) { 
    new2<- c(new2, (thisSubset$a[i]/exp(5*log(2)))) 
    } 
    #loop 3 
    new3 <- numeric(nrow(thisSubset)) 
    for (i in 1:nrow(thisSubset)) if (thisSubset$a[i] < 5) { 
     new3[i] <- thisSubset$d[i]-thisSubset$d[i+1]/2 
    } else { 
    new3[i] <- thisSubset$d[i]-thisSubset$d[i-1]/2 
    } 
    #loop x 
    #... 
} 

隨着我的數據集越來越大,處理時間呈指數增長到幾個小時。我很感激在R中有更好的方式來執行類似的任務(例如apply),但是當每個循環中發生幾件事情時,以及每行中有多個元素被使用時,我還可以使用'for'以外的任何東西嗎?如果有人能夠給我一個上面提到的每個或任何循環的例子,我將不勝感激。

+0

您可能會從查看data.table包的文檔中受益。這提供了簡單的子集和操作(一旦你瞭解了最初的語法學習曲線)。 – ds440

+0

通常,嘗試在任何循環之前移動「矢量化」的所有計算。例如。不需要遍歷每個'thisSubset $ b [i]'從5中減去它,並且在外部循環之前可以保存一個變量'5 - df $ b',以避免多次減法。 (1)'new1 = 1/exp((5 - thisSubset $ b)* log(2))',(2)'new2 = thisSubset $ a [這個子集$ c> 25]/exp(5 * log(2))'和(3)'new3 = ifelse(thisSubset $ a <5,thisSubset $ d - c(thisSubset $ d [-1],NA)/ 2,thisSubset $ d - c(NA,thisSubset $ d [-nrow(thisSubset)])/ 2)' –

+0

您能解釋一下它是如何工作的,而c(thisSubset $ d [-1],NA)'是等價的到'thisSubset $ d [i + 1]'以及'c(NA,thisSubset $ d [-nrow(thisSubset)])'等價於'thisSubset $ d [i-1]'?謝謝 – Tony

回答

0

R是一種向量化語言,因此大多數函數可以應用於整個列中的一個語句,而不必迭代每行。例如,我向量化了你的三個內部循環:

df <- data.frame(a=c(1:10), b=c(11:20), c=c(21:30), d=c(31:40)) 

thisSubset <- df 

    #loop 1 
    new1 <- numeric(nrow(thisSubset)) 
    var1 = 5 - thisSubset$b  
    new1 <- 1/exp(var1*log(2)); 

    #loop 2   
    new2 <- numeric() 
    new2<- (thisSubset$a[thisSubset$c >25]/exp(5*log(2))) 

    #loop 3 
    new3 <- numeric(nrow(thisSubset)) 
    dplus1 <-c(thisSubset$d[-1], thisSubset$d[length(thisSubset$d)]) 
    dminus1 <-c(thisSubset$d[1], thisSubset$d[-length(thisSubset$d)]) 
    new3<- ifelse((thisSubset$a < 5), thisSubset$d-dplus1/2, thisSubset$d-dminus1/2) 
    #loop x 
    #... 

你的外部循環是不必要的。
有關其他意見,請參閱Mark的解決方案。

+0

當通過向量化語句而不是循環將值分配給向量時,@MarkTimms關於預先分配的建議仍然是一種行之有效的方法?例如,在循環1和循環3中(如循環2中的執行錯誤),我們還是受益於:'new1 < - numeric(nrow(thisSubset))' – Tony

+0

是的,將值分配給預分配矢量然後連續添加其他元素。 – Dave2e

1

對於循環來說,並不比真正的應用慢(實際上,有時它們甚至可以更快!)。只要有可能,真正的速度收益來自將明確的循環轉換爲矢量化代碼。例如,環路1和環路2在代碼可以被轉換爲矢量化語句像這樣:

#loop 1 
new1 <- 1/exp((5 - thisSubset$b) * log(2)) 

#loop 2   
new2 <- thisSubset$a[thisSubset$c > 25]/exp(5 * log(2)) 

事情變得棘手一點當你的計算依賴於向量(的索引進行向量化,如在環3中),但確實存在具有各種「滾動」功能的高效實現的軟件包(有關詳細信息,請參閱rollzoo軟件包)。

當你需要使用for循環,記住,你應該總是開始迭代,而不是迭代過程中不斷增長的前預分配你的「成果」向量:

#bad 
new2 <- numeric() 
for (i in 1:nrow(thisSubset)) { 
    if (thisSubset$c[i] >25) { 
    new2<- c(new2, (thisSubset$a[i]/exp(5*log(2)))) 
    } 
} 

#good 
new2 <- numeric(length = nrow(thisSubset)) 
for (i in 1:nrow(thisSubset)) { 
    if (thisSubset$c[i] >25) { 
    new2[i] <- (thisSubset$a[i]/exp(5*log(2))) 
    } 
} 

這可以防止[R不必在每次迭代之後複製new2並導致更快的代碼。

+0

'* apply'函數並不是一些魔術子彈,它可以將任何函數變成一個更快的完全向量化的函數,但是可以加快速度從他們那裏獲得。雖然'* apply'函數是僞裝的循環,'* apply'的循環發生在'C'而不是'R','C'是一種更快的語言。如果你所調用的函數在計算上是密集型的,你可能看不到從'for'切換到'* apply'的速度,因爲在實際循環中花費的時間很少。如果功能很快,它可以產生巨大的差異。 – Barker