For 迴圈的最優構造

為了說明 good for loop 構造的效果,我們將以四種不同的方式計算每列的平均值:

  1. 使用優化不佳的 for 迴圈
  2. 使用優化的 for 迴圈
  3. 使用*apply 系列功能
  4. 使用 colMeans 功能

這些選項中的每一個都將以程式碼顯示; 將顯示執行每個選項的計算時間的比較; 最後將討論差異。

環路優化不佳

column_mean_poor <- NULL
for (i in 1:length(mtcars)){
  column_mean_poor[i] <- mean(mtcars[[i]])
}

針對迴圈進行了優化

column_mean_optimal <- vector("numeric", length(mtcars))
for (i in seq_along(mtcars)){
  column_mean_optimal <- mean(mtcars[[i]])
}

vapply 功能

column_mean_vapply <- vapply(mtcars, mean, numeric(1))

colMeans 功能

column_mean_colMeans <- colMeans(mtcars)

效率比較

對這四種方法進行基準測試的結果如下所示(程式碼未顯示)

Unit: microseconds
     expr     min       lq     mean   median       uq     max neval  cld
     poor 240.986 262.0820 287.1125 275.8160 307.2485 442.609   100    d
  optimal 220.313 237.4455 258.8426 247.0735 280.9130 362.469   100   c 
   vapply 107.042 109.7320 124.4715 113.4130 132.6695 202.473   100 a   
 colMeans 155.183 161.6955 180.2067 175.0045 194.2605 259.958   100  b

請注意,優化的 for 迴圈消除了構造不良的 for 迴圈。構造不良的 for 迴圈不斷增加輸出物件的長度,並且在每次改變長度時,R 重新評估物件的類。

通過在開始迴圈之前宣告輸出物件的型別及其長度,優化的 for 迴圈消除了一些此開銷負擔。

然而,在這個例子中,vapply 函式的使用使計算效率提高了一倍,主要是因為我們告訴 R 結果必須是數字(如果任何一個結果不是數字,則會返回錯誤)。

使用 colMeans 功能比 vapply 功能慢。這種差異可歸因於在 colMeans 中執行的一些錯誤檢查,主要是由於 vapply 函式中未執行的 as.matrix 轉換(因為 mtcarsdata.frame)。