readLines 占用太多存储空间

readLines taking up too much storage

我正在使用 readLines 读取大量带有 ; 的非常大的 .csvs。使用以下代码分隔符:

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,因为没有 headers(所以它不是真正的 csv)并且每一行都有不同的 length/number 列。

感谢您的帮助!

从您之前(已删除)的问题中 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; ...

虽然真正的 test.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 的垃圾收集器(高级编程语言的常见事物)有多快 return 内存,我也不确定根据 R 的 "global string pool".但是如果你有兴趣,你可以试试这个改编你的功能。

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

(我不保证它不会仍然“绽放”你的内存使用,但也许它不那么糟糕。)

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

dat <- lapply(files, func)