R: R CSV 文件读写:readr 完整教程
最后更新:2026-08-26
前面 10 课我们学的都是 R 内部数据,但真实项目里 99% 的数据在外部文件里。这一课我们学最常用的数据格式——CSV 文件。R 有两套 CSV 读写方案:基础 R 的
read.csv和 tidyverse 的readr,后者更智能、更快。
读完这一课你就能把 @,000 rows of的销售数据5 秒读进 R,并写出干净的报表。
1. 你将学到
- CSV 文件是什么,为什么数据科学都用它
- 基础 R
read.csvvs tidyversereadr的差异 read_csv()8 个常用参数(col_types、na、skip、n_max 等)write_csv()输出报表- 处理常见问题(编码、缺失值、分隔符)
- 大文件读取技巧(分块、类型优化)
2. 一个数据导入的痛点
(1) 痛点:Excel 转 R 卡死
小陈是分析师,manager发了 1 个 100 MB 的销售 CSV:
id,date,product,region,amount
1,2024-01-15,手机,北京,2999
2,2024-01-15,电脑,上海,5999
3,2024-01-16,手机,广州,2899
...
(@,000 rows of)
他用 read.csv() 读取,等了 30 秒,打印出 @,000 rows of又花了 10 秒,还发现 5 列全是 chr 类型(数字当字符串了)。
(2) R 的解法
# 1. 用 readr 智能读取(5 秒)
library(readr)
sales <- read_csv("sales.csv")
# 2. 自动类型推断
# id → integer, date → date, product → character,
# region → character, amount → double
# 3. 只读取需要的行(提速 3 倍)
sales_sample <- read_csv("sales.csv", n_max = 10000)
# 4. 写出干净报表
write_csv(sales_sample, "sales_clean.csv")
1 行代码 = 30 秒 → 5 秒。这就是 readr 的威力。
3. 什么是 CSV?
(1) CSV = Comma-Separated Values
姓名,年龄,城市
Alice,25,北京
Bob,30,上海
Charlie,35,广州
- 每一行 = 1 条记录
- 字段之间用
,(或;|\t)分隔 - 第一行通常是列名(header)
- 支持字符串引号
"..."处理含逗号的字段
(2) 为什么数据科学都用 CSV?
| 优点 | 说明 |
|---|---|
| 通用 | Excel、Python、R、数据库都能读 |
| 人类可读 | 记事本打开就能看 |
| 体积小 | 比 Excel 小 5-10 倍 |
| 跨平台 | 不会因 Excel 版本不同乱码 |
| 易处理 | 没有 Excel 的格式/公式噪音 |
graph LR
A[CSV 文件<br/>name,age,city] --> B[read_csv 智能读取]
B --> C[tibble 干净数据框]
C --> D[write_csv 报表]
C --> E[dplyr 分析]
C --> F[ggplot2 可视化]
style A fill:#cce5ff
style B fill:#d4edda
style C fill:#fff3cd
style D fill:#f8d7da
style E fill:#e1d4ff
style F fill:#ffe1d4
4. 基础 R vs tidyverse:两套 CSV 方案
mindmap
root((CSV 读写的<br/>两套方案))
基础 R
read.csv
write.csv
慢
字符串转 factor
tidyverse
readr
read_csv
write_csv
read_tsv
优点
智能类型推断
快 5-10x
大文件进度条
字符串不转 factor
选择建议
新项目: tidyverse
老代码: read.csv + stringsAsFactors = FALSE
GB 级数据: data.table::fread
(1) 两套方案对比
| 特性 | 基础 R read.csv |
tidyverse read_csv |
|---|---|---|
| 速度 | 慢(基 R 兼容) | 快 5-10 倍(C++ 后端) |
| 类型推断 | 弱(默认 chr) | 智能推断(int/dbl/date/lgl) |
| 进度条 | ❌ | ✅(大文件友好) |
| 字符串 | 默认转 factor | 保持字符 |
| 返回类型 | data.frame | tibble(更友好) |
| 列名处理 | 报错友好 | 自动清理(空格、特殊字符) |
| 缺失值 | NA |
NA + 自定义字符串 |
| 字符串 | stringsAsFactors = TRUE |
始终 FALSE |
(2) 实际对比
# 基础 R(老代码常见)
df_old <- read.csv("data.csv")
# 默认行为:所有字符串列转 factor(坑!)
# tidyverse(推荐)
library(readr)
df_new <- read_csv("data.csv")
# 智能推断类型,字符保持字符
read.csv 加 stringsAsFactors = FALSE。
(3) 何时用基础 R?
| 场景 | 推荐 |
|---|---|
| 新项目 / 写新代码 | read_csv (readr) |
| 处理 GB 级超大数据 | data.table::fread(更快) |
| 老代码维护 | read.csv 加 stringsAsFactors = FALSE |
| 教学 / 简单脚本 | 两者都行 |
5. read_csv() 8 个常用参数
(1) 基本语法
read_csv(file, col_types = TRUE, col_names = TRUE, na = "NA",
skip = 0, n_max = Inf, locale = default_locale(),
progress = TRUE)
(2) 参数速查表
| 参数 | 作用 | 默认值 |
|---|---|---|
file |
文件路径或 URL | 必填 |
col_types |
列类型规范 | TRUE(自动推断) |
col_names |
TRUE/向量(自定义列名) | TRUE(用第一行) |
na |
缺失值标记 | "NA" |
skip |
跳过前 N 行 | 0 |
n_max |
最大读取行数 | Inf(全部) |
locale |
区域设置(编码、小数点) | default_locale() |
progress |
显示进度条 | TRUE |
(3) 实战:常用参数
library(readr)
# 1. 基本读取(智能推断类型)
df <- read_csv("data.csv")
# 2. 指定列类型(避免推断错误)
df <- read_csv("data.csv", col_types = cols(
id = col_integer(),
date = col_date(format = "%Y-%m-%d"),
amount = col_double(),
name = col_character()
))
# 3. 跳过前 5 行注释
df <- read_csv("data.csv", skip = 5)
# 4. 自定义 NA 标记(CSV 里用 "NULL" 表示缺失)
df <- read_csv("data.csv", na = c("", "NA", "NULL", "N/A"))
# 5. 只读前 1000 行(调试用)
df <- read_csv("data.csv", n_max = 1000)
# 6. 跳过列(用 col_types 设为 col_skip())
df <- read_csv("data.csv", col_types = cols(
temp_col = col_skip()
))
(4) 智能类型推断详解
readr 的最大优势是自动推断:
| 数据示例 | 推断为 |
|---|---|
1, 2, 3 |
integer |
1.5, 2.3 |
double |
2024-01-15 |
date |
12:30:00 |
time |
TRUE, FALSE |
logical |
北京, 上海 |
character |
# 推断示例
sales <- read_csv("sales.csv")
spec(sales) # 查看推断结果
# cols(
# id = col_integer(),
# date = col_date(format = "%Y-%m-%d"),
# product = col_character(),
# region = col_character(),
# amount = col_double()
# )
6. write_csv():输出报表
(1) 基本语法
write_csv(x, file, na = "NA", append = FALSE, col_names = TRUE)
(2) 实战
# 1. 基本输出
write_csv(sales, "sales_clean.csv")
# 2. 输出到指定路径
write_csv(sales, "output/2024_sales.csv")
# 3. 追加到现有文件(不覆盖)
write_csv(new_data, "sales.csv", append = TRUE, col_names = FALSE)
# 4. 自定义 NA 表示
write_csv(df, "output.csv", na = "")
(3) ⚠️ write_csv 不会保留类型
readr 写入 CSV 时,类型信息会丢失(CSV 本身是纯文本)。再读回来要重新推断:
# 写出
write_csv(sales, "sales.csv")
# 再读回
sales_loaded <- read_csv("sales.csv")
# 类型由 read_csv 重新推断
.rds(保留类型)——见第 12 课。
7. 处理常见问题
(1) 中文编码乱码
# 1. 指定编码读取
df <- read_csv("data.csv", locale = locale(encoding = "UTF-8"))
# 或
df <- read_csv("data.csv", locale = locale(encoding = "GBK"))
# 2. 检查文件编码
guess_encoding("data.csv")
# 多数情况返回 "UTF-8" 或 "GBK"
# 3. 写入时指定编码
write_csv(df, "output.csv") # 默认 UTF-8
(2) 分隔符不是逗号
# 1. 制表符分隔(TSV)
df <- read_tsv("data.tsv") # sep = "\t"
# 2. 分号分隔(欧洲常用)
df <- read_csv2("data.csv") # sep = ";"
# 3. 自定义分隔符(如 |)
df <- read_delim("data.txt", delim = "|")
(3) 数字带千分位
# 欧洲格式:1.234,56(千分位是 .,小数是 ,)
df <- read_csv("data_eu.csv",
locale = locale(grouping_mark = ".", decimal_mark = ","))
(4) 大文件读取慢
# 1. 只读需要的列(提速 2-3 倍)
df <- read_csv("big.csv", col_select = c(id, amount))
# 2. 用 col_types 跳过列
df <- read_csv("big.csv", col_types = cols(
temp_col_1 = col_skip(),
temp_col_2 = col_skip()
))
# 3. 关闭进度条
df <- read_csv("big.csv", progress = FALSE)
# 4. 更快的方案:data.table::fread
# fread("big.csv") 比 read_csv 快 5-10 倍
8. 高级:列类型规范 cols()
(1) cols() 完整语法
col_types = cols(
id = col_integer(), # 整数
name = col_character(), # 字符
amount = col_double(), # 双精度浮点
date = col_date(), # 日期
time = col_time(), # 时间
datetime = col_datetime(),# 日期时间
is_active = col_logical(),# 逻辑
skip = col_skip(), # 跳过该列
guess = col_guess() # 让 readr 推断
)
(2) 字符串简写(更简洁)
# 用 "i" "c" "d" "D" "l" 简写
col_types = cols(
id = "i", # integer
name = "c", # character
amount = "d", # double
date = "D", # date
is_active = "l" # logical
)
# 全部推断(默认)
col_types = TRUE
# 用列表(顺序对应列)
col_types = list(
id = col_integer(),
name = col_character()
)
9. 完整示例:销售数据导入与分析
下面是一个完整工作流示例,把本课所有知识串起来。
▶ 示例:销售数据导入 + 清洗 + 输出报表
# ============================================
# 销售数据导入与处理
# 功能:读取 CSV → 清洗 → 分析 → 输出报表
# ============================================
library(readr)
library(dplyr)
# 1. 准备示例数据
sample_data <- tibble(
id = 1:10,
date = as.Date("2024-01-01") + 0:9,
product = c("手机", "电脑", "手机", "平板", "手机",
"电脑", "平板", "手机", "电脑", "手机"),
region = c("北京", "上海", "广州", "北京", "深圳",
"上海", "广州", "北京", "深圳", "上海"),
amount = c(2999, 5999, 2899, 3999, 3099,
6299, 3899, 2899, 5999, 3199),
note = c(NA, "促销", NA, NA, "新品", NA, NA, "促销", NA, NA)
)
# 写入示例 CSV(UTF-8 编码)
write_csv(sample_data, "sales_demo.csv")
cat("=== 文件已生成 ===\n")
# 2. 读取 CSV(智能推断)
sales <- read_csv("sales_demo.csv",
na = c("", "NA"),
col_types = cols(
id = col_integer(),
date = col_date(format = "%Y-%m-%d"),
product = col_character(),
region = col_character(),
amount = col_double(),
note = col_character()
))
cat("=== 读取结果 ===\n")
print(sales)
# 3. 数据质量检查
cat("\n=== 数据结构 ===\n")
str(sales)
# 4. 数据分析
cat("\n=== 销售统计 ===\n")
summary_stats <- sales |>
group_by(region) |>
summarise(
订单数 = n(),
总销售 = sum(amount),
平均订单 = round(mean(amount), 2),
最高订单 = max(amount)
) |>
arrange(desc(总销售))
print(summary_stats)
# 5. 找出促销订单
cat("\n=== 促销订单 ===\n")
promo <- sales |> filter(!is.na(note))
print(promo)
# 6. 按产品分类
cat("\n=== 产品销售 ===\n")
product_stats <- sales |>
group_by(product) |>
summarise(销量 = n(), 销售额 = sum(amount))
print(product_stats)
# 7. 输出干净报表
write_csv(summary_stats, "sales_by_region.csv")
write_csv(product_stats, "sales_by_product.csv")
cat("\n=== 报表已输出 ===\n")
cat(" - sales_by_region.csv(按地区)\n")
cat(" - sales_by_product.csv(按产品)\n")
# 8. 用 RDS 保留类型(推荐)
saveRDS(sales, "sales_clean.rds")
sales_reloaded <- readRDS("sales_clean.rds")
cat("\nRDS 加载后类型:\n")
print(sapply(sales_reloaded, class))
预期输出(节选):
=== 销售统计 ===
# A tibble: 4 × 5
region 订单数 总销售 平均订单 最高订单
<chr> <int> <dbl> <dbl> <dbl>
1 北京 3 9897 3299 3999
2 上海 3 15497 5166. 6299
3 广州 2 6798 3399 3899
4 深圳 2 9098 4549. 5999
=== 促销订单 ===
# A tibble: 2 × 6
id date product region amount note
<int> <date> <chr> <chr> <dbl> <chr>
1 2 2024-01-02 电脑 上海 5999 促销
2 8 2024-01-08 手机 北京 2899 促销
❓ 常见问题
locale = locale(encoding = "UTF-8") 或 "GBK"。不确定编码先用 guess_encoding() 检查。read_csv 是 tidyverse 标配,与 dplyr/ggplot2 整合好;fread 速度更快(GB 级数据首选),但要装 data.table 包。一般项目 read_csv 够用。📖 小节
- CSV 是最通用的数据格式:纯文本、人类可读、跨平台、Excel/Python/R/数据库都能读
- 推荐用
readr::read_csv——比基础 Rread.csv快 5-10 倍,智能推断类型,tibble 友好 - 8 个常用参数:
col_types(类型规范)、na(NA 标记)、skip、n_max、col_select、locale、progress、col_names - 自动类型推断:整数 / 双精度 / 日期 / 时间 / 日期时间 / 逻辑 / 字符
write_csv()输出报表,但不保留类型——R 内部持久化用saveRDS()/readRDS()- 中文乱码用
locale = locale(encoding = "UTF-8");分隔符不是,用read_tsv/read_delim - 大文件优化:
col_select+col_types = col_skip()+ 关闭 progress
📝 作业
-
基础题:创建一个包含 5 行 4 列(id/name/score/date)的
tibble,用write_csv()写入test.csv,再read_csv()读回,验证类型推断正确(date列是 Date 类型)。 -
基础题:读取上题的
test.csv,用col_types = cols()显式指定所有列类型(id 整数、score 双精度、name 字符、date 日期),验证类型与显式指定一致。 -
基础题:构造 1 个含 NA 的数据框(用
c(1, NA, 3, NA, 5)),写入 CSV 后用na = c("NA", "")读取,验证 NA 正确处理。 -
进阶题:模拟 100 行销售数据(product/region/amount/date),用
read_csv读入后:① 按地区分组求总销售额;② 找出金额最高的 5 个订单;③ 用write_csv输出top5.csv和by_region.csv两个报表。 -
挑战题:构造一个含特殊字符的 CSV(列名有空格、字符串含逗号、含
|分隔符),用read_csv和read_delim分别读取,对比结果差异。截图保存控制台输出。