我一直无法找出vroom
非常大的超过 RAM(gzipped)csv 文件的解决方案。但是,以下方法对我来说效果很好,我很高兴了解查询速度更快同时还节省磁盘空间的方法。
- 使用来自https://github.com/BurntSushi/xsv
split
的子命令将大型 csv 文件拆分为舒适的内存块,例如 10^5 行,并将它们保存在文件夹中。xsv
data.table::fread
使用循环逐一读取所有块(以避免内存不足错误) for
,并使用包将所有块作为压缩parquet
文件保存到文件夹中,arrow
这样可以节省空间并准备大表以进行快速查询。对于更快的操作,建议重新保存按您需要经常过滤的字段分区parquet
的文件。
- 现在,您可以使用命令使用
arrow::open_dataset
和查询该多文件 parquet 文件夹dplyr
。根据我的经验,它占用最少的磁盘空间并提供最快的结果。
我使用data.table::fread
每个字段的列类的显式定义来最快和最可靠地解析 csv 文件。readr::read_csv
也很准确,但速度较慢。但是,列类的自动分配read_csv
以及您可以自定义列类的read_csv
方式实际上是最好的 - 所以更少的人工时间但更多的机器时间 - 这意味着它可能总体上更快,具体取决于设想。其他 csv 解析器对我使用的那种 csv 文件抛出了错误并浪费了时间。
您现在可以删除包含分块 csv 文件的文件夹以节省空间,除非您想尝试使用其他 csv 解析器循环读取它们。
其他以前成功的方法:循环读取上面提到的所有 csv 块并将它们保存到:
- 使用
disk.frame
包的文件夹。然后可以使用文档中解释的命令dplyr
或data.table
命令来查询该文件夹。它可以保存在压缩fst
文件中,从而节省空间,但不如parquet
文件那么多。
DuckDB
数据库中允许使用SQL
或dplyr
命令查询的表。使用数据库表方法不会为您节省磁盘空间。但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)
如果,而不是parquet
,你想使用 -
disk.frame
,回调函数可用于创建分块压缩fst
文件dplyr
或data.table
样式查询。
DuckDB
,回调函数可用于append
分块成数据库表SQL
或dplyr
样式查询。
通过明智地选择命令chunk_size
参数readr::read_csv_chunked
,计算机在运行查询时永远不会耗尽 RAM。
PS:我gzip
对文件使用压缩,parquet
因为它们可以ParquetViewer
从https://github.com/mukunku/ParquetViewer预览。否则,zstd
(当前不支持ParquetViewer
)解压缩速度更快,从而提高读取速度。
编辑2:
我得到了一个对我的机器来说非常大的 csv 文件:20 GB gzip 压缩并扩展到大约 83 GB,而我的家用笔记本电脑只有 16 GB。原来read_csv_chunked
我在前面EDIT中提到的方法无法完成。它总是在一段时间后停止工作并且不会创建所有parquet
块。使用我以前的方法来拆分 csv 文件,xsv
然后在它们上循环创建parquet
块。公平地说,我必须提到这种方式也需要多次尝试,并且我已经编写了一个检查程序,以便在连续尝试运行程序时只创建额外的块。 parquet
编辑 3:
VROOM 在处理大文件时确实有困难,因为它需要将索引以及从文件中读取的任何数据存储在内存中。请参阅开发线程https://github.com/r-lib/vroom/issues/203
编辑4:
附加提示:通过上述方法创建的分块拼花文件可以非常方便地使用 SQL 和 DuckDB 方法进行查询,在
https://duckdb.org/docs/data/parquet
和
https://duckdb.org/2021/06中提到/25/querying-parquet.html
DuckDB 方法很重要,因为 R Arrow 方法目前受到官方文档页面https://arrow.apache.org/docs/r/articles/dataset.html中提到的非常严重的限制。
具体来说,我引用:“在当前版本中,箭头支持dplyr
动词mutate(), transmute(), select(), rename(), relocate(), filter()
,和arrange()
。聚合尚不支持,因此在调用summarise()
或其他具有聚合函数的动词之前,使用collect()
将选定的数据子集拉入内存中R 数据框。”
问题是,如果你collect()
在一个非常大的数据集上使用,RAM 使用量会激增并且系统崩溃。然而,使用 SQL 语句在与 DuckDB 相同的大数据集上执行相同的聚合作业不会导致 RAM 使用高峰,也不会导致系统崩溃。因此,在 Arrow 为大数据的聚合查询修复自身之前,来自 DuckDB 的 SQL 提供了一个很好的解决方案来查询分块拼花格式的大数据集。