如何用readr的read_csv按列位置指定列类型:默认double,部分设character
问题
我有多个CSV文件,数据具备以下特性:
- 绝大多数列为数值型;
- 不同文件中的数值列数量不固定;
- 少数列为字符型,这些字符列的数量和位置在所有文件中固定,但列名未知且不统一。
举个例子:前2列为字符型,后续是数量未知的double型,且前2列的列名在不同文件中存在差异(因此无法通过列名指定类型)。
我需要用read_csv实现以下需求:
- 指定
double()为默认列类型; - 按列位置而非列名将特定列设为
character(); - 保留文件中原有的列名;
- 禁止
read_csv猜测任何列类型,完全按照规则解析数据。
现有方案多依赖列名指定类型,无法满足需求。以下是测试示例:
生成测试CSV文件
suppressPackageStartupMessages(library(dplyr)) library(readr) write_csv(iris, 'iris1.csv') write_csv(rename(iris, species = Species), 'iris2.csv') write_csv(rename(iris, Sp = Species), 'iris3.csv')
已知可行但无法使用的方案(依赖列名)
df1 <- read_csv('iris1.csv', col_types = cols(Species = 'c', .default = 'd')) df2 <- read_csv('iris2.csv', col_types = cols(species = 'c', .default = 'd')) df3 <- read_csv('iris3.csv', col_types = cols(Sp = 'c', .default = 'd'))
但我仅知晓第4列为字符型,其余为数值型,却不知道列名,需要强制按该规则读取文件。实际场景中文件包含数十列且列数不固定,col_types = 'dddc'这类方式不可行(readr会猜测未指定的列)。
尝试过的无效写法
# 语法错误,会报错 #df1 <- read_csv('iris1.csv', col_types = cols('4' = 'c', .default = 'd')) #df2 <- read_csv('iris2.csv', col_types = cols('4' = 'c', .default = 'd')) #df3 <- read_csv('iris3.csv', col_types = cols('4' = 'c', .default = 'd'))
# 可运行但会猜测列类型,不符合需求 df1 <- read_csv('iris1.csv', col_types = '???c') df2 <- read_csv('iris2.csv', col_types = '???c') df3 <- read_csv('iris3.csv', col_types = '???c')
解决方案
可以通过先读取文件列名,再动态构建col_types参数的方式,实现按位置指定列类型的需求,具体步骤如下:
- 快速读取目标CSV的列名(不加载数据);
- 初始化所有列为默认的
double()类型,再将指定位置的列修改为character(); - 用构建好的
col_types读取完整数据。
通用函数实现
library(readr) library(purrr) read_csv_by_position <- function(file_path, char_col_positions, default_type = col_double()) { # 读取列名,不加载数据 col_names <- read_csv(file_path, n_max = 0) %>% colnames() # 初始化所有列为默认类型 col_types <- map(col_names, ~default_type) %>% set_names(col_names) # 修改指定位置的列为字符型 for(pos in char_col_positions) { if(pos <= length(col_types)) { col_types[[pos]] <- col_character() } } # 转换为cols对象 col_types <- do.call(cols, col_types) # 读取数据 read_csv(file_path, col_types = col_types) }
测试使用
针对示例中「第4列为字符型」的场景,调用函数读取三个文件:
# 读取iris1.csv,第4列为字符型 df1 <- read_csv_by_position('iris1.csv', char_col_positions = 4) # 读取iris2.csv,第4列为字符型 df2 <- read_csv_by_position('iris2.csv', char_col_positions = 4) # 读取iris3.csv,第4列为字符型 df3 <- read_csv_by_position('iris3.csv', char_col_positions = 4) # 验证列类型 sapply(df1, class) sapply(df2, class) sapply(df3, class)
说明
- 通过
read_csv(file_path, n_max = 0)获取列名,仅读取表头,效率极高; - 完全禁止readr的类型猜测逻辑,所有列类型严格按照设定规则解析;
- 保留原文件的列名,符合需求;
- 支持同时指定多个字符型列位置,比如
char_col_positions = c(1,2)可将前两列设为字符型。
内容的提问来源于stack exchange,提问作者jmuhlenkamp
相关产品推荐
相关产品推荐

