使用 vroom 在 R 中部分读取非常大 csv.gz
Partially read really large csv.gz in R using vroom
我有一个 csv.gz 文件(据我所知)在压缩前大小为 70GB。我的机器有 50GB 的内存,所以无论如何我永远无法在 R 中打开它。
我可以按如下方式加载前 10m 行:
library(vroom)
df <- vroom("HUGE.csv.gz", delim= ",", n_max = 10^7)
对于我要做的事情,一次加载10m行就可以了,做我的操作,然后继续接下来的10m行。我可以循环执行此操作。
因此我正在尝试 skip
论点。
df <- vroom("HUGE.csv.gz", delim= ",", n_max = 10^7, skip = 10^7)
这会导致错误:
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")`
我用 Sys.setenv("VROOM_CONNECTION_SIZE" = 131072*1000)
增加了它,但是,错误仍然存在。
有解决办法吗?
编辑: 我发现随机访问 gzip 压缩的 csv (csv.gz) 是不可能的。我们必须从头开始。可能最简单的就是解压保存,然后跳过就可以了。
对于非常大的 more-than-RAM(gzip 压缩)csv 文件,我无法找出 vroom
解决方案。但是,以下方法对我来说效果很好,我很高兴知道查询速度更快同时还能节省磁盘空间的方法 space.
- 使用 https://github.com/BurntSushi/xsv 中
xsv
中的 split
子命令将大型 csv 文件拆分为 comfortably-within-RAM chunks of比如 10^5 行并将它们保存在一个文件夹中。
- 使用
data.table::fread
使用 for
循环逐个读取所有块(以避免低内存错误)并将它们全部保存到文件夹中作为压缩文件 parquet
使用 arrow
包保存 space 并为快速查询准备大型 table 的文件。为了更快的操作,建议按您需要经常过滤的字段重新保存 parquet
文件 partitioned。
- 现在您可以使用
arrow::open_dataset
并使用 dplyr
命令查询该多文件 parquet 文件夹。根据我的经验,它需要最少的磁盘 space 并提供最快的结果。
我使用 data.table::fread
并显式定义每个字段的列 类 以最快、最可靠地解析 csv 文件。 readr::read_csv
也很准确,但速度较慢。但是,read_csv
自动分配列 类 以及 read_csv
自定义列 类 的方式实际上是最好的 - 人性化程度较低- 时间但更多的机器时间 - 这意味着它可能会更快,具体取决于场景。其他 csv 解析器对我使用的那种 csv 文件抛出错误,浪费时间。
您现在可以删除包含分块 csv 文件的文件夹以保存 space,除非您想用其他 csv 解析器试验循环读取它们。
其他以前成功的方法:循环读取上面提到的所有csv块并将它们保存到:
- 一个使用
disk.frame
包的文件夹。然后可以使用文档中解释的 dplyr
或 data.table
命令查询该文件夹。它具有保存压缩 fst
文件的功能,可以节省 space,但不如 parquet
文件多。
-
DuckDB
数据库中的 table 允许使用 SQL
或 dplyr
命令进行查询。使用数据库-tables 方法不会为您节省磁盘space。但是 DuckDB
还允许使用 SQL
命令查询 partitioned/un-partitioned 镶木地板文件(节省磁盘 space)。
编辑:- 改进方法如下
我做了一些实验,发现了一种更好的方法来完成上述操作。使用下面的代码,大型(压缩的)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
,回调函数可用于为 dplyr
或 data.table
样式查询创建分块压缩 fst
文件。
DuckDB
,回调函数可用于append
将数据块table用于SQL
或dplyr
样式查询。
通过明智地选择 readr::read_csv_chunked
命令的 chunk_size
参数,计算机在 运行 查询时永远不会 运行 内存不足。
PS:我对 parquet
文件使用 gzip
压缩,因为它们可以使用 https://github.com/mukunku/ParquetViewer 中的 ParquetViewer
进行预览。否则,zstd
(目前 ParquetViewer
不支持)解压缩速度更快,从而提高阅读速度。
编辑 2:
我得到了一个 csv 文件,它对我的机器来说非常大:20 GB 压缩后扩展到大约 83 GB,而我的家用笔记本电脑只有 16 GB。原来我在前面 EDIT 中提到的 read_csv_chunked
方法无法完成。它总是在一段时间后停止工作并且不会创建所有 parquet
块。使用我之前使用 xsv
拆分 csv 文件然后循环创建 parquet
块的方法。公平地说,我必须提到它也用这种方式进行了多次尝试,并且我编写了一个检查程序,以便在 运行 启动程序时仅创建 additional parquet
块连续尝试。
编辑 3:
VROOM 在处理大文件时确实有困难,因为它需要在内存中存储索引以及您从文件中读取的任何数据。查看开发线程 https://github.com/r-lib/vroom/issues/203
编辑 4:
附加提示:使用 SQL 和 DuckDB 方法可以非常方便地查询通过上述方法创建的分块 parquet 文件
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.
中提到了这一点
具体来说,我引用:“在当前版本中,arrow 支持 dplyr
动词 mutate(), transmute(), select(), rename(), relocate(), filter()
和 arrange()
。尚不支持聚合,因此在调用 summarise()
或其他具有聚合函数的动词,使用 collect()
将选定的数据子集拉入内存中的 R 数据帧。"
问题是,如果您在非常大的数据集上使用 collect()
,RAM 使用会激增并且系统会崩溃。然而,使用 SQL 语句在 DuckDB 的同一大数据集上执行相同的聚合作业不会导致 RAM 使用高峰,也不会导致系统崩溃。因此,在 Arrow 自行修复大数据的聚合查询之前,DuckDB 的 SQL 提供了一个很好的解决方案来查询分块镶木地板格式的大数据集。
我有一个 csv.gz 文件(据我所知)在压缩前大小为 70GB。我的机器有 50GB 的内存,所以无论如何我永远无法在 R 中打开它。
我可以按如下方式加载前 10m 行:
library(vroom)
df <- vroom("HUGE.csv.gz", delim= ",", n_max = 10^7)
对于我要做的事情,一次加载10m行就可以了,做我的操作,然后继续接下来的10m行。我可以循环执行此操作。
因此我正在尝试 skip
论点。
df <- vroom("HUGE.csv.gz", delim= ",", n_max = 10^7, skip = 10^7)
这会导致错误:
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")`
我用 Sys.setenv("VROOM_CONNECTION_SIZE" = 131072*1000)
增加了它,但是,错误仍然存在。
有解决办法吗?
编辑: 我发现随机访问 gzip 压缩的 csv (csv.gz) 是不可能的。我们必须从头开始。可能最简单的就是解压保存,然后跳过就可以了。
对于非常大的 more-than-RAM(gzip 压缩)csv 文件,我无法找出 vroom
解决方案。但是,以下方法对我来说效果很好,我很高兴知道查询速度更快同时还能节省磁盘空间的方法 space.
- 使用 https://github.com/BurntSushi/xsv 中
xsv
中的split
子命令将大型 csv 文件拆分为 comfortably-within-RAM chunks of比如 10^5 行并将它们保存在一个文件夹中。 - 使用
data.table::fread
使用for
循环逐个读取所有块(以避免低内存错误)并将它们全部保存到文件夹中作为压缩文件parquet
使用arrow
包保存 space 并为快速查询准备大型 table 的文件。为了更快的操作,建议按您需要经常过滤的字段重新保存parquet
文件 partitioned。 - 现在您可以使用
arrow::open_dataset
并使用dplyr
命令查询该多文件 parquet 文件夹。根据我的经验,它需要最少的磁盘 space 并提供最快的结果。
我使用 data.table::fread
并显式定义每个字段的列 类 以最快、最可靠地解析 csv 文件。 readr::read_csv
也很准确,但速度较慢。但是,read_csv
自动分配列 类 以及 read_csv
自定义列 类 的方式实际上是最好的 - 人性化程度较低- 时间但更多的机器时间 - 这意味着它可能会更快,具体取决于场景。其他 csv 解析器对我使用的那种 csv 文件抛出错误,浪费时间。
您现在可以删除包含分块 csv 文件的文件夹以保存 space,除非您想用其他 csv 解析器试验循环读取它们。
其他以前成功的方法:循环读取上面提到的所有csv块并将它们保存到:
- 一个使用
disk.frame
包的文件夹。然后可以使用文档中解释的dplyr
或data.table
命令查询该文件夹。它具有保存压缩fst
文件的功能,可以节省 space,但不如parquet
文件多。 -
DuckDB
数据库中的 table 允许使用SQL
或dplyr
命令进行查询。使用数据库-tables 方法不会为您节省磁盘space。但是DuckDB
还允许使用SQL
命令查询 partitioned/un-partitioned 镶木地板文件(节省磁盘 space)。
编辑:- 改进方法如下
我做了一些实验,发现了一种更好的方法来完成上述操作。使用下面的代码,大型(压缩的)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
,回调函数可用于为dplyr
或data.table
样式查询创建分块压缩fst
文件。DuckDB
,回调函数可用于append
将数据块table用于SQL
或dplyr
样式查询。
通过明智地选择 readr::read_csv_chunked
命令的 chunk_size
参数,计算机在 运行 查询时永远不会 运行 内存不足。
PS:我对 parquet
文件使用 gzip
压缩,因为它们可以使用 https://github.com/mukunku/ParquetViewer 中的 ParquetViewer
进行预览。否则,zstd
(目前 ParquetViewer
不支持)解压缩速度更快,从而提高阅读速度。
编辑 2:
我得到了一个 csv 文件,它对我的机器来说非常大:20 GB 压缩后扩展到大约 83 GB,而我的家用笔记本电脑只有 16 GB。原来我在前面 EDIT 中提到的 read_csv_chunked
方法无法完成。它总是在一段时间后停止工作并且不会创建所有 parquet
块。使用我之前使用 xsv
拆分 csv 文件然后循环创建 parquet
块的方法。公平地说,我必须提到它也用这种方式进行了多次尝试,并且我编写了一个检查程序,以便在 运行 启动程序时仅创建 additional parquet
块连续尝试。
编辑 3:
VROOM 在处理大文件时确实有困难,因为它需要在内存中存储索引以及您从文件中读取的任何数据。查看开发线程 https://github.com/r-lib/vroom/issues/203
编辑 4:
附加提示:使用 SQL 和 DuckDB 方法可以非常方便地查询通过上述方法创建的分块 parquet 文件 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.
中提到了这一点具体来说,我引用:“在当前版本中,arrow 支持 dplyr
动词 mutate(), transmute(), select(), rename(), relocate(), filter()
和 arrange()
。尚不支持聚合,因此在调用 summarise()
或其他具有聚合函数的动词,使用 collect()
将选定的数据子集拉入内存中的 R 数据帧。"
问题是,如果您在非常大的数据集上使用 collect()
,RAM 使用会激增并且系统会崩溃。然而,使用 SQL 语句在 DuckDB 的同一大数据集上执行相同的聚合作业不会导致 RAM 使用高峰,也不会导致系统崩溃。因此,在 Arrow 自行修复大数据的聚合查询之前,DuckDB 的 SQL 提供了一个很好的解决方案来查询分块镶木地板格式的大数据集。