編寫高效的 R 程式碼


編寫高效的程式碼非常重要,因為它可以加快開發時間,並使我們的程式易於理解、除錯和維護。我們將討論各種技術,例如基準測試、向量化和並行程式設計,以加快我們的 R 程式碼速度。如果你渴望成為一名資料科學家,就必須學習這些技術。那麼,讓我們開始吧!

基準測試

最簡單的最佳化方法之一是使用最新的 R 版本。新版本不會修改我們現有的程式碼,但它總是包含功能強大的庫函式,這些函式可以提高執行時間。

以下 R 命令顯示 R 的版本資訊列表:

print(version)

輸出

               _                                          
platform       x86_64-pc-linux-gnu                        
arch           x86_64                                     
os             linux-gnu                                  
system         x86_64, linux-gnu                          
status         Patched                                    
major          4                                          
minor          2.2                                        
year           2022                                       
month          11                                         
day            10                                         
svn rev        83330                                      
language       R                                          
version.string R version 4.2.2 Patched (2022-11-10 r83330)
nickname       Innocent and Trusting                              

將 CSV 檔案讀取為 RDS 檔案

使用 read.csv() 載入檔案需要很長時間。高效的方法是先將 .csv 檔案讀取並儲存為 .rds 格式,然後再讀取二進位制檔案。R 提供 saveRDS() 函式將 .csv 檔案儲存為 .rds 格式。

示例

考慮以下程式,該程式對以兩種不同格式(兩種格式的檔案相同)存在的檔案的讀取時間差異進行基準測試:

# Display the time taken to read file using read.csv() print(system.time(read.csv( "https://people.sc.fsu.edu/~jburkardt/data/csv/snakes_count_10000.csv"))) # Save the file in .rds format saveRDS("https://people.sc.fsu.edu/~jburkardt/data/csv/snakes_count_10000.csv", "myFile.rds" ) # Display the time taken to read in binary format print(system.time(readRDS("myFile.rds")))

輸出

 user   system  elapsed 
0.017    0.002    0.603 
 user   system  elapsed 
    0        0        0

注意兩種方法的執行時間差異。以 .RDS 格式讀取相同檔案所需的時間幾乎可以忽略不計。因此,讀取 RDS 檔案比讀取 CSV 檔案更有效率。

使用 “<-” 和 “=” 運算子進行賦值

R 提供了幾種將變數和檔案賦值給物件的方法。兩種運算子被廣泛用於此目的:“<-” 和 “=”。有趣的是,當我們在函式內部使用 “<-” 運算子時,它會建立新物件或覆蓋現有物件。由於我們要儲存結果,因此在 system.time() 函式內部使用 “<-” 運算子非常有用。

經過時間微基準測試函式

system.time() 函式可以可靠地計算某些操作所需的時間,但它有一個限制,即無法同時比較多個操作。

R 提供了一個微基準測試庫,該庫提供了一個 microbenchmark() 函式,我們可以使用該函式來比較兩個函式或操作所需的時間。

示例

考慮以下程式,該程式使用 microbenchmark() 函式來比較以兩種不同格式(CSV 和 RDS)存在的相同檔案:

library(microbenchmark) # Save the file in .rds format saveRDS("https://people.sc.fsu.edu/~jburkardt/data/csv/snakes_count_10000.csv", "myFile.rds" ) # Compare using microbenchmark() function difference <- microbenchmark(read.csv( "https://people.sc.fsu.edu/~jburkardt/data/csv/snakes_count_10000.csv"), readRDS("myFile.rds"), times = 2) # Display the time difference print(difference)

輸出

        min         lq       mean     median         uq        max neval
 405062.028 405062.028 409947.146 409947.146 414832.264 414832.264     2
     41.151     41.151    102.355    102.355    163.559    163.559     2

注意兩種方法的執行時間差異。

高效的向量化

隨著程式碼的執行,向量大小的增加在程式設計中是不可取的,應儘可能避免。這是因為它會消耗大量時間並使程式效率低下。

示例

例如,以下原始碼增加了向量的大小:

# expand() function expand <- function(n) { myVector <- NULL for(currentNumber in 1:n) myVector <- c(myVector, currentNumber) myVector } # Using system.time() function system.time(res_grow <- expand(1000))

輸出

 user  system elapsed 
0.003   0.000   0.003

正如您在輸出中看到的,expand() 函式正在消耗大量時間。

示例

我們可以透過預分配向量來最佳化上述程式碼。例如,考慮以下程式:

# expand() function expand <- function(n) { myVector <- numeric(n) for(currentNumber in 1:n) myVector[currentNumber] = currentNumber } # Using system.time() function system.time(res_grow <- expand(10000))

輸出

  user  system elapsed 
 0.001   0.000   0.001

正如您在輸出中看到的,執行時間已大大減少。

我們應該儘可能對程式碼進行向量化。

示例

例如,考慮以下程式,該程式使用簡單的迴圈方法新增向量中的值:

# Initialize a vector with random values myVector1 <- rnorm(20) # Declare another vector myVector2 <- numeric(length(myVector1)) # Compute the sum for(index in 1:20) myVector2[index] <- myVector1[index] + myVector1[index] # Display print(myVector2)

輸出

 [1]   1.31044581 -1.98035551  0.14009657 -1.62789103  1.23248277  0.49893302
 [7]  -0.53349928 -0.02553238 -0.06886832  1.16296981  0.90072271  0.20713797
[13]  -1.72293906  0.62083278  2.77900829  4.15732558  1.71227621  2.09531955
[19]  -0.06520153  0.62591177

輸出表示相應向量值本身的總和。

示例

以下程式碼執行與上述相同的操作,但這次我們將使用向量化方法,這將減少我們的程式碼大小並提高執行時間:

myVector1 <- rnorm(20) myVector2 <- numeric(length(myVector1)) # Add using vectorization myVector2 <- myVector1 + myVector1 # Display print(myVector2)

輸出

 [1] -1.0100098  3.2932186 -3.5650312 -3.2800819  0.1513545 -1.5786916
 [7]  2.0485566  2.6009810 -0.8015987 -0.6965471 -1.4298714  1.1251865
[13]  1.2536663  2.6258258  1.1093443 -1.7895628  0.3472878 -1.4783578
[19] -0.7717328 -2.2734743

輸出表示相應向量值本身的總和,但這次我們使用了向量化方法。

請注意,我們甚至可以將向量化技術應用於 R 內建函式。

示例

例如,考慮以下程式,該程式計算向量中各個值的對數:

myVector1 <- c(8, 10, 13, 16, 32, 64, 57, 88, 100, 110) myVector2 <- numeric(length(myVector1)) # Compute the sum for(index in 1:10) myVector2[index] <- log(myVector1[index]) # Display the vector print(myVector2)

輸出

[1] 2.079442 2.302585 2.564949 2.772589 3.465736 4.158883 4.043051 4.477337
[9] 4.605170 4.700480

正如您在輸出中看到的,已經顯示了相應向量值的對數。

示例

現在讓我們嘗試使用向量化技術來實現相同的結果:

myVector1 <- c(8, 10, 13, 16, 32, 64, 57, 88, 100, 110) myVector2 <- numeric(length(myVector1)) myVector2 <- log(myVector1) # Display print(myVector2)

輸出

[1] 2.079442 2.302585 2.564949 2.772589 3.465736 4.158883 4.043051 4.477337
[9] 4.605170 4.700480

正如您在輸出中看到的,已經顯示了相應向量值的的對數,但這次我們使用了向量化方法。

示例

與資料框相比,包含相同資料型別元素的矩陣具有更快的列訪問速度。例如,考慮以下程式:

library(microbenchmark) # Create a matrix myMatrix <- matrix(c(1:12), nrow = 4, byrow = TRUE) # Display print(myMatrix) # Create rows data1 <- c(1, 4, 7, 10) data2 <- c(2, 5, 8, 11) data3 <- c(3, 6, 9, 12) # Create a dataframe myDataframe <- data.frame(data1, data2, data3) # Display the dataframe print(microbenchmark(myMatrix[,1], myDataframe[,1]))

輸出

     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9
[4,]   10   11   12
Unit: nanoseconds
             expr  min     lq    mean median   uq   max neval
    myMatrix[, 1]  493  525.0  669.64  595.5  661  5038   100
 myDataframe[, 1] 6880 7110.5 8003.56 7247.0 7437 53752   100

您可以發現矩陣和資料框的列訪問方法的執行時間差異。

用於高效 R 程式碼的並行程式設計

R 提供了一個並行包,我們可以使用它來編寫高效的 R 程式碼。並行化在大多數情況下都有利於在更短的時間內完成工作,並充分利用系統資源。R 中的並行包為我們提供了 parApply() 函式,該函式使用以下步驟並行執行程式:

  • 使用 makeCluster() 函式建立一個叢集。

  • 編寫一些語句。

  • 最終,使用 stopCluster() 函式停止叢集。

示例

以下原始碼使用 R 中的 parApply() 函式計算所有列的平均值:

library(parallel) library(microbenchmark) # Create rows data1 <- c(1, 4, 7, 10) data2 <- c(2, 5, 8, 11) data3 <- c(3, 6, 9, 12) # Create a dataframe myDataframe <- data.frame(data1, data2, data3) # Create a cluster cluster <- makeCluster(2) # Apply parApply() function print(parApply(cluster, myDataframe, 2, mean)) # Stop the cluster stopCluster(cluster)

輸出

data1 data2 data3 
  5.5   6.5   7.5

正如您在輸出中看到的,已經使用並行程式設計計算了相應列的平均值,這更快。

結論

在本文中,我們簡要討論瞭如何在 R 中編寫高效的程式碼。我們討論了基準測試、不同的向量化技術和並行程式設計。我希望本教程肯定幫助您擴充套件了在資料科學領域的知識。

更新於:2023年1月17日

232 次瀏覽

啟動您的職業生涯

完成課程獲得認證

開始
廣告