使用 vroom 在 R 中部分读取非常大的 csv.gz

Mar*_*tin 5 csv r vroom

我有一个 csv.gz 文件(据我所知)压缩前的大小为 70GB。我的机器有 50GB 的 RAM,所以无论如何我永远无法在 R 中打开它。

例如,我可以按如下方式加载前 10m 行:

library(vroom)

df <- vroom("HUGE.csv.gz", delim= ",", n_max = 10^7)
Run Code Online (Sandbox Code Playgroud)

对于我必须做的事情,可以一次加载 10m 行,执行我的操作,然后继续接下来的 10m 行。我可以循环执行此操作。

因此,我正在尝试skip论证。

df <- vroom("HUGE.csv.gz", delim= ",", n_max = 10^7, skip = 10^7)
Run Code Online (Sandbox Code Playgroud)

这会导致错误:

Error: The size of the connection buffer (131072) was not large enough
to fit a complete line:
  * Increase it by setting `Sys.setenv("VROOM_CONNECTION_SIZE")`
Run Code Online (Sandbox Code Playgroud)

我增加了这个Sys.setenv("VROOM_CONNECTION_SIZE" = 131072*1000),但是,错误仍然存​​在。

这个问题有方法解决吗?

编辑:我发现随机访问 gzip 压缩的 csv (csv.gz) 是不可能的。我们必须从头开始。可能最简单的方法是解压并保存,然后跳过就可以了。

San*_*San 12

我一直无法找到vroom针对非常大的超过 RAM(gzip 压缩)的 csv 文件的解决方案。但是,以下方法对我来说效果很好,我很高兴了解具有更好查询速度同时还能节省磁盘空间的方法。

  1. 使用https://github.com/BurntSushi/xsvsplit的子命令将大型 csv 文件拆分为RAM 内舒适的块,例如 10^5 行,并将它们保存在文件夹中。xsv
  2. data.table::fread使用循环逐一读取所有块(以避免内存不足错误) for,并使用包将所有块作为压缩parquet文件保存到文件夹中arrow,这样可以节省空间并为快速查询准备大表。为了更快的操作,建议重新保存按需要频繁过滤的字段分区的文件parquet
  3. 现在,您可以使用命令arrow::open_dataset来使用和查询该多文件 parquet 文件夹dplyr。根据我的经验,它占用最少的磁盘空间并提供最快的结果。

我使用data.table::fread每个字段的列类的显式定义来实现最快、最可靠的 csv 文件解析。readr::read_csv也很准确但速度较慢。然而,自动分配列类read_csv以及自定义列类的方式read_csv实际上是最好的 - 因此更少的人工时间但更多的机器时间 - 这意味着它可能总体上更快,具体取决于设想。其他 csv 解析器对我使用的 csv 文件类型抛出了错误,浪费了时间。

现在,您可以删除包含分块 csv 文件的文件夹以节省空间,除非您想尝试使用其他 csv 解析器循环读取它们。

其他先前成功的方法:循环读取上述所有 csv 块并将它们保存到:

  1. 使用disk.frame包的文件夹。然后可以使用文档中解释的命令dplyr查询该文件夹。data.table它可以保存在压缩fst文件中,从而节省空间,尽管不如parquet文件那么多。
  2. 数据库中的一个表DuckDB,允许使用SQLdplyr命令进行查询。使用数据库表方法不会节省磁盘空间。但DuckDB也允许使用命令查询分区/未分区的 parquet 文件(这可以节省磁盘空间)SQL

编辑:-下面的改进方法

我做了一些实验,发现了一种更好的方法来完成上述操作。使用下面的代码,大型(压缩)csv 文件将在 R 环境中自动分块(无需使用任何外部工具,如xsv),并且所有块将以格式写入parquet准备查询的文件夹中。

library(readr)
library(arrow)

fyl <- "...path_to_big_data_file.csv.gz"
pqFolder <- "...path_to_folder_where_chunked_parquet_files_are_to_be_saved"

f <- function(x, pos){
  write_parquet(x,
                file.path(pqFolder, paste0(pos, ".parquet")),
                compression = "gzip",
                compression_level = 9)
}

read_csv_chunked(
  fyl,
  col_types = list(Column1="f", Column2="c", Column3="T", ...), # all column specifications
  callback = SideEffectChunkCallback$new(f),
  chunk_size = 10^6)
Run Code Online (Sandbox Code Playgroud)

如果parquet您想使用 - 而不是 -

  1. disk.frame,回调函数可用于创建分块压缩fst文件dplyrdata.table样式查询。
  2. DuckDB,回调函数可用于将append块放入数据库表中以进行SQLdplyr样式查询。

通过明智地选择命令chunk_size参数readr::read_csv_chunked,计算机在运行查询时永远不会耗尽 RAM。

PS:我gzip对文件使用压缩,因为可以从https://github.com/mukunku/ParquetViewerparquet预览它们。否则,(当前不支持)解压缩速度更快,从而提高读取速度。ParquetViewerzstdParquetViewer

编辑2:

我得到了一个 csv 文件,该文件对于我的机器来说非常大:压缩后为 20 GB,扩展到大约 83 GB,而我的家用笔记本电脑只有 16 GB。事实证明,read_csv_chunked我在前面的编辑​​中提到的方法未能完成。它总是在一段时间后停止工作并且不会创建所有parquet块。使用我之前分割 csv 文件xsv然后循环创建parquet块的方法是有效的。公平地说,我必须提到,这种方式也进行了多次尝试,并且我编写了一个检查,以便在连续尝试运行程序时仅创建额外的块。 parquet

编辑3:

VROOM 在处理大文件时确实有困难,因为它需要将索引以及从文件中读取的任何数据存储在内存中。请参阅开发线程https://github.com/r-lib/vroom/issues/203

编辑4:

附加提示:通过上述方法创建的分块 parquet 文件可以使用 SQL 和 https://duckdb.org/docs/data/parquethttps://duckdb.org/2021/06中提到的 DuckDB 方法非常方便地查询/25/querying-parquet.html

DuckDB 方法很重要,因为 R Arrow 方法目前受到非常严重的限制,官方文档页面https://arrow.apache.org/docs/r/articles/dataset.html中提到了这一点。

具体来说,我引用:“在当前版本中,arrow 支持dplyr动词mutate(), transmute(), select(), rename(), relocate(), filter()、 和arrange()。尚不支持聚合,因此在调用summarise()或其他具有聚合函数的动词之前,请使用collect()将选定的数据子集拉入内存中R数据框。”

问题是,如果您使用collect()非常大的数据集,RAM 使用量会激增并且系统崩溃。而使用 SQL 语句与 DuckDB 对同一大数据集执行相同的聚合作业不会导致 RAM 使用峰值,也不会导致系统崩溃。因此,在 Arrow 修复大数据聚合查询之前,DuckDB 的 SQL 提供了一个很好的解决方案来查询分块 parquet 格式的大数据集。

  • 这是一个非常好的答案,我特别喜欢“read_csv_chunked”提示以及使用“arrow”包来创建镶木地板数据集。关于您在写入过程中遇到的问题:根据几张 Arrow 工单,“read_csv_chunked”冻结的原因与 Windows 上 R Arrow 包中的多线程问题有关。(据说)这将在软件包的 6.0.0 版本中得到修复。同时,您可以通过使用“options(arrow.use_threads = FALSE)”或“set_io_thread_count(1)”和“set_cpu_count(1)”禁用多线程来避免问题。 (3认同)