未记录事实核验 · 状态:维护中
记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。
warning 适用边界与维护说明
本文适用于学习 R 多格式读写、列类型与编码控制、数据库分批查询及基础 API 请求,不是可直接部署的生产数据接入方案。本次仅作结构审阅,未进行外部事实核验或代码运行验证;性能表、提速数字及按文件大小给出的选型建议只能作为评估起点,不能作为实测排名或容量保证。示例文件、数据库表和 API 地址需自行准备,远程 dplyr 查询需 dbplyr 支持,命令读取示例依赖相应系统工具。API 重试仅展示基础机制,实际接入仍需补充认证、状态分类、限流响应与结果结构检查;其中“非2xx 自动抛出错误”的注释不应作为严格成功状态校验的依据。write_excel_csv 的 BOM 有助于 Excel 识别编码,但效果仍受版本和导入方式影响。
R 数据导入导出
数据分析的起点是把数据读进来,终点是把结果输出去。R 生态为各种格式提供了成熟的读写工具。本文覆盖 CSV/文本、Excel、大文件(data.table)、数据库(DBI)、API(httr + jsonlite)以及 R 原生格式,并给出各方案的性能对比和选型建议。
目录
- CSV 与文本文件
- Excel 文件
- 大文件高速读取:data.table::fread
- 数据库连接
- API 数据获取
- R 原生格式:RDS 与 RData
- 编码问题处理
- 读取方式性能对比
- 格式选型指南
CSV 与文本文件
基础包 read.csv / read.table
R 内置的读取函数,无需安装额外包,适合小型文件的快速读取。
# read.csv 是 read.table 的特化版(sep=",",header=TRUE)
df <- read.csv("data.csv")
# read.table 参数全集(最常用的几个)
df <- read.table(
file = "data.txt",
header = TRUE, # 第一行是否为列名,默认FALSE
sep = "\t", # 分隔符:"\t"=制表符,","=逗号," "=空格
fileEncoding = "UTF-8", # 输入文件编码,用于读取时转码
na.strings = c("", "NA", "NULL", "N/A", "缺失"), # 哪些字符串视为NA
colClasses = c("character", "numeric", "Date"), # 预设列类型(加速+防猜错)
nrows = 10000, # 只读前N行(调试或抽样时有用)
skip = 2, # 跳过开头N行
quote = '"', # 引号字符
comment.char = "#", # 注释行标记
stringsAsFactors = FALSE # R3.x 必须显式设置
)
tip `colClasses` 参数是加速 `read.table` 的关键技巧。预设列类型可减少类型推断工作;若以“100 万行 CSV 提速 30% 以上”为测试假设,应在实际数据和运行环境中测量,本文未提供支持该数值的基准。
readr 包:更快更智能的现代替代
readr 是 tidyverse 的文件读写组件,相比基础包有三个核心优势:更快(C++ 实现)、类型推断更准确、进度条可视化。
library(readr)
# read_csv 返回 tibble(而非 data.frame)
df <- read_csv("data.csv")
# 关键参数详解
df <- read_csv(
"data.csv",
col_names = TRUE, # 第一行为列名(默认TRUE)
col_types = cols( # 精确指定列类型
id = col_character(),
age = col_integer(),
salary = col_double(),
date = col_date(format = "%Y-%m-%d"),
active = col_logical()
),
na = c("", "NA", "null"),
skip = 0,
n_max = Inf, # 最大读取行数
locale = locale(
encoding = "UTF-8", # 文件编码
decimal_mark = ".", # 小数点符号(欧洲数据常用",")
grouping_mark = "," # 千分位符号
)
)
# 快捷函数
read_tsv("data.tsv") # 制表符分隔
read_delim("data.txt", delim="|") # 自定义分隔符
# 输出
write_csv(df, "output.csv")
write_csv(df, "output.csv", na = "") # NA 输出为空字符串而非"NA"
col_types 规范速查:
| 函数 | 类型 | 示例格式 |
|---|---|---|
col_character() |
字符串 | - |
col_integer() |
整数 | 123 |
col_double() |
浮点数 | 3.14 |
col_logical() |
逻辑值 | TRUE/FALSE/T/F/1/0 |
col_date(format) |
日期 | "%Y-%m-%d" |
col_datetime(format) |
日期时间 | "%Y-%m-%d %H:%M:%S" |
col_skip() |
跳过此列 | - |
col_guess() |
自动推断(默认) | - |
Excel 文件
readxl 读取
library(readxl)
# 基础读取(默认第一个工作表)
df <- read_excel("data.xlsx")
# 完整参数
df <- read_excel(
path = "data.xlsx",
sheet = "销售数据", # 工作表名(或1,2等序号)
range = "A1:F100", # 精确读取范围(Cell Range)
col_names = TRUE,
col_types = c("text", "numeric", "date", "guess", "guess", "guess"), # 与 A1:F100 的六列对应,类型需按实际数据调整;可用 "skip" 跳过列
na = c("", "NA"),
skip = 3, # 未指定 range 时跳过前3行;本例指定 range 后,skip 和 n_max 不控制读取边界
n_max = Inf,
trim_ws = TRUE # 自动去除字符串两端空格
)
# 列出所有工作表
excel_sheets("data.xlsx")
# 批量读取所有工作表
sheets <- excel_sheets("data.xlsx")
all_data <- lapply(sheets, function(s) read_excel("data.xlsx", sheet = s))
names(all_data) <- sheets
# 写入 Excel
library(writexl)
write_xlsx(df, "output.xlsx")
# 写入多个 Sheet
write_xlsx(list("Sheet1" = df1, "Sheet2" = df2), "multi_sheet.xlsx")
note readxl 不依赖 Java(不像 xlsx 包),安装更简单,跨平台兼容性更好。如果需要设置单元格格式、公式、样式,改用 `openxlsx` 包。
大文件高速读取:data.table::fread
当文件超过 500MB 或行数超过 500 万时,可优先评估 fread,同时检查可用内存和数据类型。它支持自动检测分隔符和多线程读取;“比 read.csv 快 10–50 倍”仅可作为待测试的假设,本文未提供支持这一范围的基准。
library(data.table)
# 最简用法(自动检测分隔符)
dt <- fread("bigdata.csv")
# 关键参数
dt <- fread(
input = "bigdata.csv",
sep = "auto", # 自动检测,也可指定","或"\t"
header = TRUE,
nThread = 8, # 并行线程数(建议设为 CPU 核心数)
nrows = -1L, # -1L 表示全部行
skip = 0,
select = c("id","name","salary"), # 只读指定列(节省内存)
# drop = c("col_to_drop"), # 如需排除列,先移除上面的 select 参数,再启用 drop
colClasses = list(
character = "id",
integer = "age"
),
na.strings = c("", "NA"),
encoding = "UTF-8", # 或 "Latin-1"
verbose = FALSE # 是否打印读取信息
)
# fread 返回 data.table,可无缝转成 data.frame
df <- as.data.frame(dt)
# 写入(fwrite 同样多线程,速度极快)
fwrite(dt, "output.csv", nThread = 8)
fread 的特殊能力:
# 直接读取命令输出(管道输入)
dt <- fread(cmd = "grep '2024' large_log.txt")
# 直接读取 URL
dt <- fread("https://example.com/data.csv")
# 读取压缩文件(需要系统有 zcat/gzip)
dt <- fread(cmd = "zcat data.csv.gz")
数据库连接
R 通过 DBI(DataBase Interface)包提供统一的数据库操作接口,底层驱动按数据库类型选择。
连接 - 查询 - 断开的标准模式
library(DBI)
library(RSQLite) # SQLite 驱动
# 1. 建立连接
con <- dbConnect(
drv = SQLite(),
dbname = "mydb.sqlite"
)
# MySQL 连接(需要 RMySQL 或 RMariaDB 包)
# con <- dbConnect(RMySQL::MySQL(),
# host = "192.168.1.100",
# port = 3306,
# dbname = "sales_db",
# user = "analyst",
# password = Sys.getenv("DB_PASSWORD") # 密码从环境变量读取,不硬编码
# )
# PostgreSQL 连接(需要 RPostgres 包)
# con <- dbConnect(RPostgres::Postgres(),
# host = "localhost", dbname = "mydb",
# user = "postgres", password = "xxx"
# )
# 2. 探查数据库结构
dbListTables(con) # 列出所有表
dbListFields(con, "users") # 列出 users 表的所有字段
dbExistsTable(con, "sales") # 检查表是否存在
# 3. 查询数据
# 小结果集:一次性读取
df <- dbGetQuery(con, "SELECT * FROM users WHERE age > 30 LIMIT 100")
# 大结果集:分批读取
res <- dbSendQuery(con, "SELECT * FROM transactions WHERE year = 2024")
while (!dbHasCompleted(res)) {
chunk <- dbFetch(res, n = 10000) # 每次读1万行
# 处理 chunk...
}
dbClearResult(res)
# 读取整张表
df <- dbReadTable(con, "products")
# 4. 写入数据
dbWriteTable(con, "cleaned_data", df,
overwrite = TRUE, # 覆盖已有表
append = FALSE # 追加到已有表
)
# 执行 DDL/DML
dbExecute(con, "CREATE INDEX idx_user_id ON orders(user_id)")
dbExecute(con, "UPDATE users SET status='active' WHERE last_login > '2024-01-01'")
# 5. 断开连接(必须执行,否则连接池泄漏)
dbDisconnect(con)
warning 永远不要在代码中硬编码数据库密码。使用 `Sys.getenv("DB_PASSWORD")` 从环境变量读取,或用 `keyring` 包管理凭证。
结合 dplyr 的懒查询
library(dplyr)
con <- dbConnect(SQLite(), "mydb.sqlite")
users_tbl <- tbl(con, "users") # 创建远程表引用(不立即执行)
result <- users_tbl %>%
filter(age > 25) %>%
select(name, salary) %>%
arrange(desc(salary)) %>%
head(100)
# 查看 dplyr 生成的 SQL
show_query(result)
# 真正执行并拉到内存
df <- collect(result)
dbDisconnect(con)
API 数据获取
httr + jsonlite 的标准模式
library(httr)
library(jsonlite)
# GET 请求
resp <- GET(
url = "https://api.example.com/v1/data",
query = list(city = "Beijing", limit = 100), # URL 查询参数
add_headers(
Authorization = paste("Bearer", Sys.getenv("API_TOKEN")),
Accept = "application/json"
),
timeout(30) # 30秒超时
)
# 检查状态码
stop_for_status(resp) # 非2xx 自动抛出错误
# 解析响应
raw_text <- content(resp, "text", encoding = "UTF-8")
data <- fromJSON(raw_text) # 解析 JSON
df <- as.data.frame(data$results) # 取出数据字段
# POST 请求(提交 JSON 体)
resp_post <- POST(
url = "https://api.example.com/v1/query",
body = list(start_date = "2024-01-01", metrics = c("sales", "orders")),
encode = "json", # 自动序列化为 JSON
add_headers(Authorization = paste("Bearer", Sys.getenv("API_TOKEN")))
)
基础 API 重试封装(示例,不构成生产可用性保证)
# 带重试逻辑的安全请求函数
safe_api_get <- function(url, params = list(), max_retries = 3) {
for (attempt in 1:max_retries) {
tryCatch({
resp <- GET(url, query = params, timeout(30))
stop_for_status(resp)
return(fromJSON(content(resp, "text", encoding = "UTF-8")))
}, error = function(e) {
message(sprintf("第%d次尝试失败:%s", attempt, e$message))
if (attempt < max_retries) Sys.sleep(2 ^ attempt) # 指数退避
else stop(e)
})
}
}
R 原生格式:RDS 与 RData
CSV 和 Excel 无法保留 R 对象的完整状态(因子水平、属性、模型对象等)。R 原生格式解决了这个问题。
saveRDS / readRDS(推荐)
一次保存一个对象,加载时可以赋给任意变量名:
# 保存(自动 gzip 压缩)
saveRDS(model_object, "model_v1.rds")
saveRDS(large_df, "data_cleaned.rds", compress = "xz") # 更高压缩率
# 读取(赋给新变量名,不污染全局环境)
my_model <- readRDS("model_v1.rds")
df <- readRDS("data_cleaned.rds")
# 验证完整性保留
x <- factor(c("低","中","高"), levels = c("低","中","高"), ordered = TRUE)
saveRDS(x, "factor_test.rds")
y <- readRDS("factor_test.rds")
identical(x, y) # TRUE:水平、顺序、类型全部保留
save / load(多对象打包)
同时保存多个对象到一个 .RData 文件,加载时变量名自动恢复:
# 保存多个对象
save(df_train, df_test, model, params, file = "experiment_01.RData")
# 加载(自动在当前环境创建 df_train, df_test, model, params)
load("experiment_01.RData")
# 保存整个工作区(不推荐,边界模糊)
save.image("workspace.RData")
warning `load()` 会直接向当前环境注入变量,可能覆盖同名对象,且你无法提前知道里面有什么。优先使用 `readRDS()`,它的行为更可预测。
RDS vs RData 对比:
| 特性 | saveRDS / readRDS | save / load |
|---|---|---|
| 保存对象数 | 单个 | 多个 |
| 加载时变量名 | 自由指定 | 固定为保存时的名字 |
| 推荐场景 | 模型、数据框的持久化 | 打包整个实验状态 |
| 文件扩展名 | .rds |
.RData / .rda |
编码问题处理
中文数据是 R 用户绕不开的问题,核心是 UTF-8 与 GBK 的互转。
# 1. 读取时指定编码
df_utf8 <- read.csv("data_utf8.csv", fileEncoding = "UTF-8")
df_gbk <- read.csv("data_gbk.csv", fileEncoding = "GBK") # Windows 中文常见
# readr 版(推荐)
df <- read_csv("data.csv", locale = locale(encoding = "GBK"))
# 2. iconv() 转换字符向量编码
x_gbk <- "\xd5\xc5\xc8\xfd" # GBK 编码的"张三"
x_utf8 <- iconv(x_gbk, from = "GBK", to = "UTF-8")
# 转换整列
df$name <- iconv(df$name, from = "GBK", to = "UTF-8")
# 3. 写出时指定编码
write.csv(df, "output.csv", fileEncoding = "UTF-8", row.names = FALSE)
# row.names=FALSE 是写 CSV 必须加的参数,否则会多出一列行号
# 4. 检测文件编码(依赖 readr 的探测)
guess_encoding("unknown_encoding.csv")
# 5. Windows 打开 UTF-8 CSV 乱码的解决方法
# 用 write_excel_csv(自动在文件头加 BOM 标记)
write_excel_csv(df, "for_excel.csv") # 添加 UTF-8 BOM,有助于 Excel 识别编码;效果仍取决于版本及导入方式
tip 默认编码取决于操作系统、区域设置和 R 运行环境;Windows 中文环境中的旧文件可能使用 GBK,现代环境也可能使用 UTF-8,不能仅凭操作系统判断文件编码。在团队协作中,统一约定用 UTF-8,并在读写时显式指定,可避免大多数乱码问题。
读取方式性能对比
以下数值仅作为读取 100 万行 × 10 列 CSV 文件(假设约 80MB)的性能比较示意,不是已提供测试依据的典型耗时;列内容会影响文件大小,实际耗时和内存峰值需自行测量:
| 函数 | 包 | 耗时(参考值) | 内存峰值 | 返回类型 |
|---|---|---|---|---|
read.csv() |
base | ~25s | 高 | data.frame |
read_csv() |
readr | ~3s | 中 | tibble |
fread() |
data.table | ~0.5s | 低 | data.table |
fread(nThread=8) |
data.table | ~0.2s | 低 | data.table |
note 上表数值是待测试的示意假设,不代表实测量级或工具排名。具体耗时和内存峰值取决于硬件、数据内容、操作系统及读取参数;可在实际文件上比较 `read_csv` 与 `fread`,结合类型控制和使用便利性选择工具,不能仅凭文件超过 100MB 判断速度优势。
选型建议:
- 文件 < 50MB:
read_csv(),易用、类型智能 - 文件 50MB - 1GB:
fread(),速度优先 - 文件 > 1GB:
fread(nThread=n)或直接用数据库/数据湖 - 需要精确列类型控制:
read_csv(col_types=...)或fread(colClasses=...)
格式选型指南
| 格式 | 写入函数 | 读取函数 | 适用场景 | 缺点 |
|---|---|---|---|---|
| CSV | write_csv() |
read_csv() / fread() |
数据交换、跨语言 | 类型信息丢失、大文件慢 |
| Excel | write_xlsx() |
read_excel() |
与业务人员协作 | 不适合大数据、商业软件 |
| RDS | saveRDS() |
readRDS() |
R 对象持久化 | 仅 R 可读 |
| RData | save() |
load() |
多对象打包 | 加载时自动注入变量 |
| SQLite | dbWriteTable() |
dbGetQuery() |
本地结构化存储 | 并发写入有限 |
| JSON | write_json() |
fromJSON() |
API 数据、嵌套结构 | 大数据效率低 |
掌握检查
- [ ]
read.csv的colClasses参数有什么作用?什么情况下必须使用? - [ ]
read_csv和read.csv的主要区别有哪些? - [ ]
fread(nThread=8)比单线程快的原理是什么? - [ ]
dbGetQuery和dbSendQuery + dbFetch各适合什么场景? - [ ]
saveRDS和save在加载时的行为有何本质区别?
练习题
题一(基础):从网络读取一个公开 CSV 数据集(如 UCI 机器学习库的任意数据集),用 read_csv 读入,检查列类型推断是否正确,对类型有误的列用 col_types 修正,最后用 write_csv 保存,用 fread 重新读入,对比两种读法的耗时(用 system.time())。
题二(进阶):在 R 中创建一个 SQLite 数据库,将两张有关联的数据表写入(如用户表和订单表),然后用 SQL JOIN 查询每个用户的订单总金额,结果用 collect() 拉到内存,最后 saveRDS 保存为 .rds 文件。
关联文档:R数据结构 | dplyr数据操作 | 数据清洗技巧
创建时间:2024-12-11 | 更新时间:2026-03-21 预计学习时长:60-75分钟
#R语言 #数据导入 #数据导出 #CSV #Excel #数据库 #API #readr #data.table #DBI #编码