【问题标题】:Strategies for reading in CSV files in pieces?分段读取 CSV 文件的策略?
【发布时间】:2012-03-10 07:22:12
【问题描述】:

我的计算机上有一个中等大小的文件 (4GB CSV),但没有足够的 RAM 来读取它(在 64 位 Windows 上为 8GB)。过去我只是将它加载到集群节点上并读入,但我的新集群似乎任意将进程限制为 4GB 的 RAM(尽管硬件每台机器有 16GB),所以我需要一个短期修复.

有没有办法将 CSV 文件的一部分读入 R 以适应可用内存限制?这样我可以一次读取文件的三分之一,将其子集到我需要的行和列,然后再读取下一个三分之一?

感谢评论者指出我可以使用一些大内存技巧读取整个文件: Quickly reading very large tables as dataframes in R

我可以想到一些其他解决方法(例如,在一个好的文本编辑器中打开,删除 2/3 的观察结果,然后在 R 中加载),但如果可能的话,我宁愿避免它们。

因此,目前看来,分段阅读仍然是最好的方式。

【问题讨论】:

  • 这里已经详细讨论过了,特别是JD Long的回答很有用:stackoverflow.com/questions/1727772/…
  • 抱歉,这确实回答了第一个问题。显然我的 SO search-fu 需要磨练,因为我搜索但找不到它。但是,第二个问题没有得到解答:如何分段读取 .CSV 文件。
  • @mdsumner 很有趣。看起来我可以使用 read.csv.ffdf() 。想在链接的问题中回答这个问题,以便我可以投票吗? :-)
  • 短期修复可能是询问您的集群管理员如何在集群节点上保留超过 4GB 的空间!在我们的集群上,它只是将##BSUB -m 8G(或其他东西)粘贴到您的批处理提交文件中,然后等待稍长一点的时间让两个插槽在同一个节点上空闲。
  • 如果您需要更多,我们的集群有 40 个节点,每个节点都有 96GB RAM。我认为我们的集群管理员可能正在补偿一些东西。

标签: r bigdata


【解决方案1】:

在查看此主题后,我注意到未提及此问题的显着解决方案。使用连接!

1) 打开到您的文件的连接

con = file("file.csv", "r")

2) 使用 read.csv 读取代码块

read.csv(con, nrows="CHUNK SIZE",...)

旁注:定义 colClasses 将大大加快速度。确保将不需要的列定义为 NULL。

3) 做你需要做的事

4) 重复。

5) 关闭连接

close(con)

这种方法的优点是连接。如果您省略这一步,它可能会减慢速度。通过手动打开连接,您实际上打开了数据集并且在调用 close 函数之前不要关闭它。这意味着当您遍历数据集时,您将永远不会失去您的位置。假设您有一个包含 1e7 行的数据集。还假设您想一次加载一大块 1e5 行。由于我们打开连接,我们通过运行 read.csv(con, nrow=1e5,...) 获得前 1e5 行,然后为了获得第二个块,我们也运行 read.csv(con, nrow=1e5,...),依此类推......

如果我们不使用连接,我们将以相同的方式获得第一个块,read.csv("file.csv", nrow=1e5,...),但是对于下一个块,我们需要read.csv("file.csv", skip = 1e5, nrow=2e5,...)。显然这是低效的。我们必须重新找到 1e5+1 行,尽管我们只是在 1e5 行中读取。

最后,data.table::fread 很棒。但是你不能通过它连接。所以这种方法行不通。

我希望这对某人有所帮助。

更新

人们一直在为这篇文章投票,所以我想我会再补充一个简短的想法。新的readr::read_csvread.csv 一样,可以传递连接。但是,advertised 大约快 10 倍。

【讨论】:

  • data.table 的 fread 计划在下一个稳定版本中支持连接,更多细节在data.table#561
  • 请注意,iotools 包的 read* 函数可以提供连接。
  • 大的 csv 文件通常被 gzip 压缩,在这种情况下连接应该被指定为con = gzfile("file.csv.gz","r")
【解决方案2】:

例如,您可以使用 RSQLite 将其读入数据库,然后使用 sql 语句获取一部分。

如果您只需要一个部分,那么 sqldf 包中的read.csv.sql 会将数据读入 sqlite 数据库。首先,它为您创建数据库,并且数据不会通过 R,因此 R 的限制将不适用(在这种情况下主要是 RAM)。其次,将数据加载到数据库后,sqldf将指定的sql语句的输出读入R,最后销毁数据库。根据它处理您的数据的速度,如果您有多个部分,您可能只需为每个部分重复整个过程。

只需一行代码即可完成所有三个步骤,因此只需尝试一下即可。

DF <- read.csv.sql("myfile.csv", sql=..., ...other args...)

请参阅 ?read.csv.sql?sqldf 以及 sqldf home page

【讨论】:

  • 非常酷。尽管读取整个文件并转储大部分文件,但似乎仍然效率低下。它确实表明我可以将其子集化为我想要的 SQL 状态,这可能会解决我的问题。
  • 如果您只需要将其子集到一组特定的行,那么您可以使用read.table(..., skip = ..., nrows = ...)
  • 我忘记了。哇,真的有一个问题失败的一天。但我从中学到了两个新东西(ff 包和sqldf 都有过滤选项),所以也许值得。
  • 如果它本身没有关闭连接然后尝试closeAllConnections()
  • 根据我的经验,DuckDB 的性能优于 RSQLiteread.csv.sql,后者也在后台运行 SQLite
猜你喜欢
  • 2019-09-04
  • 2023-03-03
  • 2021-12-10
  • 2019-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-25
相关资源
最近更新 更多