使用R的Arrow包合并含缺失值多CSV文件报错求助
问题背景
有13个超大CSV文件(kinder_2011.csv至kinder_2023.csv),列名及顺序完全一致,但部分列存在整文件缺失值(如kinder_2011.csv的month列全为NA)。使用arrow和tidyverse编写合并代码时,出现以下错误:
Error in
compute.Dataset():
! Invalid: Could not open CSV input source 'C:/Users/XXXX/Dropbox/ChileEduc/data/intermediate/kinder/kinder_2013.csv': Invalid: In CSV column #26: Row #7: CSV conversion error to int32: invalid value ''
ℹ If you have supplied a schema and your data contains a header row, you should supply the argumentskip = 1to prevent the header being read in as data.
Runrlang::last_trace()to see where the error occurred.
错误核心:空字符串('')无法被arrow自动解析为int32类型的缺失值,导致类型转换失败。
解决方法
方法1:修改arrow读取参数,识别空字符串为缺失值
在open_delim_dataset中新增na = c("", "NA")参数,让arrow将空字符串和标准NA识别为缺失值,同时保留预定义schema确保数据类型一致。此方法无需加载全部数据到内存,适合超大文件:
library(arrow) library(tidyverse) # 保留原schema定义 schema <- schema( year = int32(), month = int32(), mrun = int32(), sex_stud = int32(), birth_date_stud = int32(), school_id = int32(), RBD = int32(), ID_ESTAB_J = int32(), ID_ESTAB_I = int32(), NOM_ESTAB = utf8(), COD_REG_ESTAB = int32(), COD_PRO_ESTAB = int32(), COD_COM_ESTAB = int32(), NOM_REG_ESTAB = utf8(), NOM_REG_A_ESTAB = utf8(), NOM_PRO_ESTAB = utf8(), NOM_COM_ESTAB = utf8(), COD_DEPROV_ESTAB = int32(), NOM_DEPROV_ESTAB = utf8(), latitude = float64(), longitude = float64(), rural_establishment = int32(), ORIGEN = int32(), DEPENDENCIA = int32(), NIVEL1 = int32(), NIVEL2 = int32(), COD_ENSE1_M = int32(), COD_GRADO_M = int32(), LET_CUR_M = utf8(), COD_TIP_CUR_M = int32(), COD_DEPE1_M = int32(), COD_ENSE2_M = int32(), ESTADO_ESTAB_M = int32(), CORR_GRU_J = int32(), COD_PROG_J = int32(), DESC_PROG_J = utf8(), COD_NIVEL_J = int32(), DESC_NIVEL_J = utf8(), COD_MODAL_J = int32(), DESC_MODAL_J = utf8(), COD_JOR_J = int32(), NOM_JOR_J = utf8(), DESC_MOD_I = utf8(), DESC_NIV_I = utf8(), COD_NIVEL_I = int32(), COD_GRUPO_I = int32(), TIPO_SOSTENEDOR = int32(), FORMAL = int32(), ASIS_REAL_J = int32(), ASIS_POTEN_J = int32(), DIAS_TRAB_GRUPO_J = int32() ) # 读取数据集,指定空字符串为缺失值 kinder <- open_delim_dataset( paste0(getwd(), "/ChileEduc/data/intermediate/kinder"), delim = ";", schema = schema, skip = 1, na = c("", "NA") # 新增参数:将空字符串和NA识别为缺失值 ) # 导出为Parquet(推荐,压缩率高、适合大数据) write_parquet(kinder, paste0(getwd(), "/ChileEduc/data/final/kinder_combined.parquet")) # 若需CSV格式(不推荐,体积大),可执行: # write_csv(as.data.frame(kinder), paste0(getwd(), "/ChileEduc/data/final/kinder_combined.csv"))
方法2:单文件读取并处理异常值(适合排查问题)
如果方法1仍报错,可逐个读取文件,手动处理数值列的异常值后再合并,确保每个文件的数据类型匹配:
library(arrow) library(tidyverse) # 获取所有目标文件路径 file_paths <- list.files( paste0(getwd(), "/ChileEduc/data/intermediate/kinder"), pattern = "kinder_\\d{4}\\.csv", full.names = TRUE ) # 定义单文件处理函数:转换异常值为NA,匹配数据类型 process_file <- function(file_path) { df <- read_delim(file_path, delim = ";", skip = 1, na = c("", "NA")) # 将非字符型的字符列转为整数,经纬度转为浮点型 df <- df %>% mutate( across(where(is.character) & !matches("^NOM_|^DESC_|^LET_CUR_M"), ~as.integer(.)), across(c(latitude, longitude), as.double) ) return(df) } # 批量处理并合并所有文件 kinder_combined <- map_dfr(file_paths, process_file) # 导出为Parquet write_parquet(kinder_combined, paste0(getwd(), "/ChileEduc/data/final/kinder_combined.parquet"))
关键注意事项
- 优先使用Parquet格式:列式存储,压缩率远高于CSV,后续读取和分析速度更快,适合900万条观测的数据集。
- 避免全量加载到内存:直接对
arrow的Dataset对象执行write_parquet,无需转为data.frame,减少内存占用。 - 排查单个文件:若仍有错误,单独读取
kinder_2013.csv查看第26列(NIVEL2)的异常值,确认是否存在非空字符串的无效值,手动清理后再合并。
内容的提问来源于stack exchange,提问作者Jorge Paredes

