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)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容