关于R中不同CSV读取函数内存占用差异的技术咨询
不同CSV读取函数的内存占用差异原因及优化建议
在R中使用base::read.csv()、readr::read_csv()、data.table::fread()和arrow::read_csv_arrow()读取同一个CSV文件时,发现生成的对象内存占用差异极大。具体测试代码及结果如下:
library(nycflights13) library(readr) library(data.table) library(arrow) library(dplyr) library(lobstr) # 导出测试数据 fl_original = nycflights13::flights fwrite(fl_original, 'nycflights13_flights.csv') # 用不同函数读取 fl_baseR = read.csv('nycflights13_flights.csv') fl_readr = readr::read_csv('nycflights13_flights.csv') fl_data.table = data.table::fread('nycflights13_flights.csv') fl_arrow = arrow::read_csv_arrow('nycflights13_flights.csv') # 查看内存占用 lobstr::obj_size(fl_baseR) # 33.12 MB lobstr::obj_size(fl_readr) # 51.43 MB lobstr::obj_size(fl_data.table) # 32.57 MB lobstr::obj_size(fl_arrow) # 21.56 MB # 查看对象类型 class(fl_baseR) # "data.frame" class(fl_readr) # "spec_tbl_df" "tbl_df" "tbl" "data.frame" class(fl_data.table) # "data.table" "data.frame" class(fl_arrow) # "tbl_df" "tbl" "data.frame"
可以看到arrow::read_csv_arrow()读取的数据内存仅为readr::read_csv()的~42%,尽管所有对象都包含data.frame类。以下是差异原因分析及读取建议:
内存差异的核心原因
1. 数据类型的精准选择与压缩
- Arrow的类型优化:
arrow::read_csv_arrow()默认会为数值列选择更紧凑的存储类型,比如用float32(单精度浮点数)代替R原生的double(双精度浮点数),用int32代替冗余的宽位整数类型,在保证数据精度足够的前提下大幅降低内存占用。而readr::read_csv()为避免数据溢出风险,默认采用更宽泛的数值类型,内存开销更大。 - 字符列的编码优化:Arrow对重复率高的字符列会自动采用字典编码,进一步压缩内存体积;而readr默认以原生字符向量存储,无这类优化。
2. 对象结构与元数据开销
- readr的元数据冗余:
readr::read_csv()返回的spec_tbl_df对象会额外存储列类型推断的元数据(即spec属性),这部分信息会占用额外内存;而Arrow返回的tbl_df元数据更精简,仅保留必要的结构信息。 - data.table的结构优化:
data.table采用了更高效的内存布局,相比标准data.frame减少了部分对象属性的开销,因此内存占用略低于base R的read.csv()结果。
3. 底层内存管理机制
Arrow基于Apache Arrow的列存储格式,内存中数据的存储更连续、紧凑,避免了R原生数据结构(如向量)的额外内存开销;而readr、base R、data.table都是基于R的原生对象系统,每个向量都包含类型信息、长度等额外属性,累加起来会增加内存占用。
读取CSV的优化建议
- 优先使用Arrow处理大型数据集:
arrow::read_csv_arrow()内存效率最高,同时支持open_dataset()实现懒加载,无需一次性将全量数据加载到内存,适合超大型CSV文件。 - 手动指定类型优化readr内存占用:如果需要兼容tidyverse生态,可以通过
col_types参数手动设置更紧凑的类型,例如:fl_readr_optimized = readr::read_csv( 'nycflights13_flights.csv', col_types = cols( dep_time = col_integer(), arr_time = col_integer(), dep_delay = col_float(), arr_delay = col_float() ) ) - data.table兼顾速度与内存:
data.table::fread()读取速度快,内存效率接近base R,适合需要快速读取且内存要求不极致的场景,可通过colClasses参数指定类型进一步优化。 - base R的优化方式:使用
read.csv()时,通过指定colClasses参数明确列类型,避免自动推断带来的冗余,同时保持stringsAsFactors=FALSE减少不必要的因子转换开销。
内容的提问来源于stack exchange,提问作者Miao Cai
相关产品推荐
相关产品推荐

