我有多个巨大的CSV文件,我必须基于Apache Parquet
导出,并基于多个条件/键(=列值)将它们拆分为更小的文件。据我所知,Apache arrow
是R包,允许使用Apache parquet
文件。
我在一个共享的实验室环境中工作,考虑到有限的RAM内存(与在同一环境中同时工作的用户数量相比),建议我们在本地SQLite数据库中创建 Dataframe ,而不是将它们导入内存(RAM)。
下面的伪代码显示了如何将CSV文件导入到本地SQLite数据库中,在下面的代码中我使用了sqldf
和tidyverse
包。
input_file_path <- "D:/tmp/mydata.csv"
db_file_path <- "D:/tmp/db_tmp_sqlite.db"
unlink(db_file_path)
sqldf(str_c("attach '", db_file_path, "' as new"))
sqldf(read.csv.sql(
file = input_file_path,
sql = "
create table mytable as
select
. . .
from
file
",
`field.types` = list(
. . .
),
##
header = TRUE,
sep = ",",
eol = "\n",
dbname = db_file_path,
drv = "SQLite"
))
这和预期的一样好用,我的表被创建,我可以运行所有需要的SQL查询,特别是添加补充变量(我的表中的列),这些变量稍后将用作将我的表导出为Apache Parquet格式的键。但是,基于Apache Arrow for R Cheatsheet,允许基于Apache Parquet
导出我的数据的函数write_dataset
需要一个dataframe。
这正是我的问题,因为R中的 Dataframe 在内存中,而我前面解释的数据在SQLite本地数据库中,这意味着首先我必须执行SELECT将整个数据导出到RAM中,类似于
df <- sqldf("select * from mytable", dbname = ...)
只有这样,我才能使用write_dataset
,并将创建的df
Dataframe 作为其第一个参数,以便基于Apache Parquet
导出和拆分数据。但这不是我想要做的。考虑到共享环境中现有的资源限制(内存不足),关键是将数据放在SQLite中,而不是放在内存(RAM)中。
有没有办法在R程序中直接从SQLite转换到Apache Parquet,而不在导出之前先将整个数据放入 Dataframe 中,或者我正在尝试做一些根本不可能的事情?
1条答案
按热度按时间brvekthn1#
DuckDB有几个很棒的特性,包括能够 * 本机 * 导入和导出CSV和 parquet 格式,而不会影响R内存。
TL; DR
仅此而已。数据永远不会导入到R中。(现在,DuckDB是否可以在不耗尽内存的情况下自己完成是我没有在本地验证的另一个问题...)
duckdb
以一种"懒惰"的方式轻松地完成,而不必将整个框架加载到R中。3我鼓励你阅读更多关于原生查询CSV/parquet文件的文档(不必加载到R中)。方法
为了比较这两种方法(通过
data.frame
(您不想这样做)和通过duckdb
),我们将使用"RSS"(来自ps::ps_memory_info()
)来指示当前R进程内存使用情况。尽管这是对R的真实影响的不完美度量,但它确实表明使用DuckDB时对R的影响要小得多。
另外,每个方法都是在
R --vanilla
的一个新示例中完成的,没有加载.Rprofile
或site-init文件,您看到的代码就是执行的代码,仅此而已。通过 Dataframe 输入R
这表示在读取全部数据后,R增加了1490MB *(仅供参考,
data.table::fread
代替read.csv
只增加了408MB内存,同样的情况。不过,我并不想优化这部分:-)(FYI,这些数字在不同的运行中会有所不同,而且可能会因本答案范围之外的其他因素而有所不同。我的笔记本电脑有64GB内存,可能无法与您所看到的完全相比。)
DuckDB,从CSV读取,写入 parquet
显示在此过程中只有23MB。
比较结果文件。
较大的文件是由于下面提到的类中的差异。我的猜测是,如果我们 * 强制 * 一些
character
列为logical
,那么它的文件大小可能会减小。更深入地了解一下内容:
一些有趣的事情推断出:
character
;ds1
中的logical
列和ds2
中的character
列都为空(抱歉,这是我的数据);它们是不同的类这一事实表明duckdb
默认为字符串形式的null而不是"bit",这可能是也可能不是您的考虑因素;numeric
-和-integer
;V11
是真正的整数,这没问题;第二个V42
显示用于区分numeric
和integer
的启发式算法遗漏了一些东西。V42
中包含小数部分的第一行位于第37159行。修复数据差异
列
V42
表明我们需要非常清楚地知道什么是进入和离开那个 parquet 生成器的。我猜这是在"CSV导入"步骤中,所以查看CSV Loading表明需要更改SAMPLE_SIZE
。虽然效率相对较低,我将使用-1
来指示它需要查看列中的所有值来确定其类。验证该假设:
的确,
V11
仍然很好,V42
从int
变成了num
。在使用这个新参数重新运行之后,
离线验证确认所有值均正确。