博客 / 詳情

返回

R語言之處理大型數據集的策略

文章和代碼已經歸檔至【Github倉庫:https://github.com/timerring/dive-into-AI 】或者公眾號【AIShareLab】回覆 R語言 也可獲取。

在實際的問題中,數據分析者面對的可能是有幾十萬條記錄、幾百個變量的數據集。處理這種大型的數據集需要消耗計算機比較大的內存空間,所以儘可能使用 64 位的操作系統和內存比較大的設備。否則,數據分析可能要花太長時間甚至無法進行。此外,處理數據的有效策略可以在很大程度上提高分析效率。

1. 清理工作空間

為了在數據分析時獲得儘可能大的內存空間,建議在啓動任何新的分析項目時,首先清理工作空間。

# rm(list = ls(all = TRUE))

函數 ls( ) 用於顯示當前工作空間中的對象,其中參數 all 默認為 FALSE,這裏設為 TRUE 是為清除包括隱藏對象在內的所有對象。

此外,在數據分析的過程中,對於臨時對象和不再需要的對象,使用命令 rm(object1,object2, …) 及時將它們清除。

2. 快速讀取.csv 文件

.csv 文件佔用空間小,可以由 Excel 查看和生成,因此被廣泛運用於存儲數據。在前面裏介紹的函數 read.csv( ) 可以很方便地讀取 .csv 文件。但是,對於大型數據集,該函數讀取數據的速度太慢,有時甚至會報錯。這時,可以使用 readr 包裏的 read_csv( ) 函數或者 data.table 包裏的 fread( ) 函數讀入數據,其中後者的讀取速度更快(大約為前者的兩倍)

data.table 包提供了一個數據框的高級版本,大大提高了數據處理的速度。該包尤其適合那些需要在內存中處理大型數據集(比如 1GB~100GB)的用户。不過,這個包的操作方式與 R 中其他包相差較大,需要投入一定的時間學習。

3. 模擬一個大型數據集

為了便於説明,下面模擬一個大型數據集,該數據集包含 50000 條記錄、200 個變量。

bigdata <- as.data.frame(matrix(rnorm(50000 * 200), ncol = 200))
# 使用了嵌套的兩個 for 循環語句和 R 的內置常量 letters(小寫英文字母)為 200 個變量命名。
varnames <- NULL
# 外面一層循環語句構建變量名的第一個字符(a~t)
for (i in letters[1:20]) {
# 裏面一層循環語句把數字 1~10 用 `_` 作為分隔符分別連接到這些字母上。
  for (j in 1:10) {
  # 函數 paste( ) 用於連接字符串。
    varnames <- c(varnames, paste(i, j, sep = "_"))
  }
}
names(bigdata) <- varnames
names(bigdata)

如果你不太想使用多個循環,可以考慮:

# 可惜 apply 此處會導致多餘的空格
# apply(expand.grid(1:20, letters[1:20]), 1, function(x) paste(x[2], x[1], sep="_")) 
# sprintf("%s_%s", expand.grid(1:10,letters[1:20])[,2],expand.grid(1:10,letters[1:20])[,1])

# 或者
# as.vector(t(outer(letters[1:20], 1:10, paste, sep="_")))

4. 剔除不需要的變量

在進行正式的分析之前,我們需要把暫時用不上的變量剔除以減少內存的負擔。dplyr 包的 select 系列函數在這裏可以派上用場,尤其是將這些函數與 tidyselect 包的 starts_with( )、ends_with( ) 和 contains( ) 等函數聯合使用會帶來諸多便利。

先加載這兩個包:

library(dplyr)
library(tidyselect)

接下來舉例説明如何使用 select 系列函數選擇或剔除變量。

subdata1 <- select(bigdata, starts_with("a"))
names(subdata1)
# 'a_1''a_2''a_3''a_4''a_5''a_6''a_7''a_8''a_9''a_10'
subdata2 <- select(bigdata, ends_with("2"))
names(subdata2)
#'a_2''b_2''c_2''d_2''e_2''f_2''g_2''h_2''i_2''j_2''k_2''l_2''m_2''n_2''o_2''p_2''q_2''r_2''s_2''t_2'

函數 starts_with( ) ends_with( ) 分別表示變量的前綴和後綴。在上面的命令中,subdata1 選取了數據集裏所有以 a 開頭的變量,而 subdata2 選取了數據集裏所有以 2 結尾的變量。

如果要選取所有以 ab 開頭的變量,可以使用下面的命令:

# subdata3 <- select(bigdata, c(starts_with("a"), starts_with("b")))
subdata3 <- select_at(bigdata, vars(starts_with("a"), starts_with("b"))) # 注意跟 select 語法稍有不同
names(subdata3)

要選擇變量名裏包含某些字符的所有變量,可以藉助函數 contains( ) 實現。例如,要選擇包含字符 1 的所有變量,可以輸入下面的命令:

# subdata4 <- select(bigdata, c(contains("1")))
subdata4 <- select_at(bigdata, vars(contains("1")))
names(subdata4)

需要注意的是,所有以 10 結尾的變量也是包含字符 1 的。

如果要剔除某些變量,只需要在函數 starts_with( )、ends_with( ) 和 contains( ) 前面加上 - 號。例如,要剔除以 15 結尾的變量,可以使用下面的命令:

# subdata5 <- select(bigdata, c(-contains("1"), -contains("5")))
subdata5 <- select_at(bigdata, vars(-contains("1"), -contains("5")))
names(subdata5)

5. 選取數據集的一個隨機樣本

對大型數據集的全部記錄進行處理往往會降低分析的效率。在編寫代碼時,可以只抽取一部分記錄對程序進行測試,以便優化代碼並消除 bug。

# 參數 size 用於指定行的個數
sampledata1 <- sample_n(subdata5, size = 500)
nrow(sampledata1)
# 參數 size 用於指定佔所有行的比例。
sampledata2 <- sample_frac(subdata5, size = 0.02)
nrow(sampledata2)
# 500
# 1000

函數 sample_n( ) 和 sample_frac( ) 都用於從數據框中隨機選取指定數量的行,前者中的參數 size 用於指定行的個數,而後者中的參數 size 用於指定佔所有行的比例。

需要説明的是,上面討論的處理大型數據集的策略只適用於處理 GB 級的數據集。不論用哪種工具,處理 TB 和 PB 級的數據集都是一種挑戰。R 中有幾個包可以用於處理 TB 級數據集,例如 RHIPE、RHadoop 和 RevoScaleR 等。這些包的學習曲線相對陡峭,需要對高性能計算有一定的瞭解,有需求的話你可以自行探索,這裏不做介紹。

sample_n() 和 sample_frac() 即將退休,包文檔中推薦改用 slice_sample( ),用法可查看此處。
# 使用 slice_sample( ) 進行處理
sampledata1 <- slice_sample(subdata5, n = 500)
nrow(sampledata1)
sampledata2 <- slice_sample(subdata5, prop = 0.02)
nrow(sampledata2)
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.