首页 > 解决方案 > readLines 占用太多存储空间

问题描述

readLines过去常常用 ; 读取一堆非常大的 .csv。使用以下代码的分隔符:

read_p <- function(flnm) {
  readLines(flnm)  %>%  str_split(pattern = "; ") 
}

for (i in 1:length(files)){
  dat[[i]] <- read_p(files[[i]])
}

files文件名向量在哪里。代码本身运行得相当快,但它在 R 中占用大约 4GB,而在文件夹中只占用约 500MB - 我在阅读它时是否遗漏了什么以避免?我需要使用 readLines 因为没有标题(所以它不是真正的 csv),并且每行都有不同的长度/列数。

谢谢你的帮助!

标签: rreadlines

解决方案


test.csv从您之前的(自删除后)问题开始操作,转换为numeric.

作为记录,该文件看起来像

996; 1160.32; 1774.51; 4321.05; 2530.97; 2817.63; 1796.18; ...
1008; 1774.51; 1796.18; 1192.42; 1285.69; 1225.96; 2229.92; ...
1020; 1796.18; 1285.69; 711.67; 1761.44; 1016.74; 1671.90; ...
1032; 1285.69; 1761.44; 1016.74; 1671.90; 725.51; 2466.49; ...
1044; 1761.44; 1016.74; 725.51; 2466.49; 661.82; 1378.85; ...
1056; 1761.44; 1016.74; 2466.49; 661.82; 1378.85; 972.94; ...
1068; 2466.49; 661.82; 1378.85; 972.94; 2259.46; 3648.49; ...
1080; 2466.49; 1378.85; 972.94; 2259.46; 1287.72; 1074.63; ...

尽管 realtest.csv有 751 行文本,并且每行都有 10001-10017;分隔的字段。这个(未删节的)文件不到 64 MiB。

读入它,解析它,然后转换为数字对其对象大小有显着影响:

object.size(aa1 <- readLines("test.csv"))
# Warning in readLines("test.csv") :
#   incomplete final line found on 'test.csv'
# 67063368 bytes

object.size(aa2 <- strsplit(aa1, "[; ]+"))
# 476021832 bytes

object.size(aa3 <- lapply(aa2, as.numeric))
# 60171040 bytes

我们最终得到:

length(aa3)
# [1] 751

str(aa3[1:4])
# List of 4
#  $ : num [1:10006] 996 1160 1775 4321 2531 ...
#  $ : num [1:10008] 1008 1775 1796 1192 1286 ...
#  $ : num [1:10009] 1020 1796 1286 712 1761 ...
#  $ : num [1:10012] 1032 1286 1761 1017 1672 ...

因此,将其读入全长字符串并不会导致内存爆炸,而是将其拆分为每行 10000 多个字段。这是因为每个character对象的开销更大:

### vec-1, nchar-0
object.size("")
# 112 bytes

### vec-5, nchar-0
object.size(c("","","","",""))
# 152 bytes

### vec-5, nchar-10
object.size(c("aaaaaaaaaa","aaaaaaaaaa","aaaaaaaaaa","aaaaaaaaaa","aaaaaaaaaa"))
# 160 bytes

如果我们查看原始数据,我们会看到爆炸式增长:

object.size(aa1[1])   # whole lines at a time, so one line is 10000+ characters
# 89312 bytes
object.size(aa2[[1]]) # vector of 10000+ strings, each between 3-8 characters
# 633160 bytes

但幸运的是,内存中的数字要小得多:

object.size(1)
# 56 bytes
object.size(c(1,2,3,4,5))
# 96 bytes

它的扩展性要好得多。显然,将 R 存储中的数据从 453MiB(拆分,字符串)减少到 57MiB(拆分,数字)已经足够好。


在读取这些文件时,您仍然会看到 R 的内存使用量激增。您可以尝试通过在之后立即转换为数字来减少它strsplit;老实说,我不知道 R 的垃圾收集器(对于高级编程语言来说很常见)返回内存的速度有多快,我也不确定根据 R 的“全局字符串池”这将如何表现。但是如果你有兴趣,你可以试试这个你的函数的改编。

func <- function(path) {
  aa1 <- readLines(path)
  aa2 <- lapply(aa1, function(st) as.numeric(strsplit(st, "[; ]+")[[1]]))
  aa2
}

(我不保证它不会仍然“增加”您的内存使用量,但也许它不会那么糟糕。)

然后你的循环的规范替换for(虽然那个循环很好),是

dat <- lapply(files, func)

推荐阅读